jsc3d-js-rails 1.6.5

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.
@@ -0,0 +1,1459 @@
1
+ /**
2
+ * @preserve Copyright (c) 2011~2013 Humu <humu2009@gmail.com>
3
+ * This file is part of jsc3d project, which is freely distributable under the
4
+ * terms of the MIT license.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+
25
+
26
+ /**
27
+ @namespace JSC3D
28
+ */
29
+ var JSC3D = JSC3D || {};
30
+
31
+ /**
32
+ * The following part of the file is a modified copy of hammer.js, which is
33
+ * released by Jorik Tangelder <j.tangelder@gmail.com> under the terms of the
34
+ * MIT license.
35
+ */
36
+
37
+ /*
38
+ * ! Hammer.JS - v1.0.5 - 2013-04-07
39
+ * http://eightmedia.github.com/hammer.js
40
+ *
41
+ * Copyright (c) 2013 Jorik Tangelder <j.tangelder@gmail.com>;
42
+ * Licensed under the MIT license
43
+ */
44
+
45
+ (function(window, undefined) {
46
+ 'use strict';
47
+
48
+ /**
49
+ * Hammer
50
+ * use this to create instances
51
+ * @param {HTMLElement} element
52
+ * @param {Object} options
53
+ * @returns {Hammer.Instance}
54
+ * @constructor
55
+ */
56
+ var Hammer = function(element, options) {
57
+ return new Hammer.Instance(element, options || {});
58
+ };
59
+
60
+ // default settings
61
+ Hammer.defaults = {
62
+ // add styles and attributes to the element to prevent the browser from doing
63
+ // its native behavior. this doesnt prevent the scrolling, but cancels
64
+ // the contextmenu, tap highlighting etc
65
+ // set to false to disable this
66
+ stop_browser_behavior: {
67
+ // this also triggers onselectstart=false for IE
68
+ userSelect: 'none',
69
+ // this makes the element blocking in IE10 >, you could experiment with the value
70
+ // see for more options this issue; https://github.com/EightMedia/hammer.js/issues/241
71
+ touchAction: 'none',
72
+ touchCallout: 'none',
73
+ contentZooming: 'none',
74
+ userDrag: 'none',
75
+ tapHighlightColor: 'rgba(0,0,0,0)'
76
+ }
77
+
78
+ // more settings are defined per gesture at gestures.js
79
+ };
80
+
81
+ // detect touchevents
82
+ Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled;
83
+ Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);
84
+
85
+ // dont use mouseevents on mobile devices
86
+ Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
87
+ Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && navigator.userAgent.match(Hammer.MOBILE_REGEX);
88
+
89
+ // eventtypes per touchevent (start, move, end)
90
+ // are filled by Hammer.event.determineEventTypes on setup
91
+ Hammer.EVENT_TYPES = {};
92
+
93
+ // direction defines
94
+ Hammer.DIRECTION_DOWN = 'down';
95
+ Hammer.DIRECTION_LEFT = 'left';
96
+ Hammer.DIRECTION_UP = 'up';
97
+ Hammer.DIRECTION_RIGHT = 'right';
98
+
99
+ // pointer type
100
+ Hammer.POINTER_MOUSE = 'mouse';
101
+ Hammer.POINTER_TOUCH = 'touch';
102
+ Hammer.POINTER_PEN = 'pen';
103
+
104
+ // touch event defines
105
+ Hammer.EVENT_START = 'start';
106
+ Hammer.EVENT_MOVE = 'move';
107
+ Hammer.EVENT_END = 'end';
108
+
109
+ // hammer document where the base events are added at
110
+ Hammer.DOCUMENT = document;
111
+
112
+ // plugins namespace
113
+ Hammer.plugins = {};
114
+
115
+ // if the window events are set...
116
+ Hammer.READY = false;
117
+
118
+ /**
119
+ * setup events to detect gestures on the document
120
+ */
121
+ function setup() {
122
+ if(Hammer.READY) {
123
+ return;
124
+ }
125
+
126
+ // find what eventtypes we add listeners to
127
+ Hammer.event.determineEventTypes();
128
+
129
+ // Register all gestures inside Hammer.gestures
130
+ for(var name in Hammer.gestures) {
131
+ if(Hammer.gestures.hasOwnProperty(name)) {
132
+ Hammer.detection.register(Hammer.gestures[name]);
133
+ }
134
+ }
135
+
136
+ // Add touch events on the document
137
+ Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_MOVE, Hammer.detection.detect);
138
+ Hammer.event.onTouch(Hammer.DOCUMENT, Hammer.EVENT_END, Hammer.detection.detect);
139
+
140
+ // Hammer is ready...!
141
+ Hammer.READY = true;
142
+ }
143
+
144
+ /**
145
+ * create new hammer instance
146
+ * all methods should return the instance itself, so it is chainable.
147
+ * @param {HTMLElement} element
148
+ * @param {Object} [options={}]
149
+ * @returns {Hammer.Instance}
150
+ * @constructor
151
+ */
152
+ Hammer.Instance = function(element, options) {
153
+ var self = this;
154
+
155
+ // setup HammerJS window events and register all gestures
156
+ // this also sets up the default options
157
+ setup();
158
+
159
+ this.element = element;
160
+
161
+ // start/stop detection option
162
+ this.enabled = true;
163
+
164
+ // merge options
165
+ this.options = Hammer.utils.extend(
166
+ Hammer.utils.extend({}, Hammer.defaults),
167
+ options || {});
168
+
169
+ // add some css to the element to prevent the browser from doing its native behavoir
170
+ if(this.options.stop_browser_behavior) {
171
+ Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);
172
+ }
173
+
174
+ // start detection on touchstart
175
+ Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) {
176
+ if(self.enabled) {
177
+ Hammer.detection.startDetect(self, ev);
178
+ }
179
+ });
180
+
181
+ // return instance
182
+ return this;
183
+ };
184
+
185
+
186
+ Hammer.Instance.prototype = {
187
+ /**
188
+ * bind events to the instance
189
+ * @param {String} gesture
190
+ * @param {Function} handler
191
+ * @returns {Hammer.Instance}
192
+ */
193
+ on: function onEvent(gesture, handler){
194
+ var gestures = gesture.split(' ');
195
+ for(var t=0; t<gestures.length; t++) {
196
+ this.element.addEventListener(gestures[t], handler, false);
197
+ }
198
+ return this;
199
+ },
200
+
201
+
202
+ /**
203
+ * unbind events to the instance
204
+ * @param {String} gesture
205
+ * @param {Function} handler
206
+ * @returns {Hammer.Instance}
207
+ */
208
+ off: function offEvent(gesture, handler){
209
+ var gestures = gesture.split(' ');
210
+ for(var t=0; t<gestures.length; t++) {
211
+ this.element.removeEventListener(gestures[t], handler, false);
212
+ }
213
+ return this;
214
+ },
215
+
216
+
217
+ /**
218
+ * trigger gesture event
219
+ * @param {String} gesture
220
+ * @param {Object} eventData
221
+ * @returns {Hammer.Instance}
222
+ */
223
+ trigger: function triggerEvent(gesture, eventData){
224
+ // create DOM event
225
+ var event = Hammer.DOCUMENT.createEvent('Event');
226
+ event.initEvent(gesture, true, true);
227
+ event.gesture = eventData;
228
+
229
+ // trigger on the target if it is in the instance element,
230
+ // this is for event delegation tricks
231
+ var element = this.element;
232
+ if(Hammer.utils.hasParent(eventData.target, element)) {
233
+ element = eventData.target;
234
+ }
235
+
236
+ element.dispatchEvent(event);
237
+ return this;
238
+ },
239
+
240
+
241
+ /**
242
+ * enable of disable hammer.js detection
243
+ * @param {Boolean} state
244
+ * @returns {Hammer.Instance}
245
+ */
246
+ enable: function enable(state) {
247
+ this.enabled = state;
248
+ return this;
249
+ }
250
+ };
251
+
252
+ /**
253
+ * this holds the last move event,
254
+ * used to fix empty touchend issue
255
+ * see the onTouch event for an explanation
256
+ * @type {Object}
257
+ */
258
+ var last_move_event = null;
259
+
260
+
261
+ /**
262
+ * when the mouse is hold down, this is true
263
+ * @type {Boolean}
264
+ */
265
+ var enable_detect = false;
266
+
267
+
268
+ /**
269
+ * when touch events have been fired, this is true
270
+ * @type {Boolean}
271
+ */
272
+ var touch_triggered = false;
273
+
274
+
275
+ Hammer.event = {
276
+ /**
277
+ * simple addEventListener
278
+ * @param {HTMLElement} element
279
+ * @param {String} type
280
+ * @param {Function} handler
281
+ */
282
+ bindDom: function(element, type, handler) {
283
+ var types = type.split(' ');
284
+ for(var t=0; t<types.length; t++) {
285
+ element.addEventListener(types[t], handler, false);
286
+ }
287
+ },
288
+
289
+
290
+ /**
291
+ * touch events with mouse fallback
292
+ * @param {HTMLElement} element
293
+ * @param {String} eventType like Hammer.EVENT_MOVE
294
+ * @param {Function} handler
295
+ */
296
+ onTouch: function onTouch(element, eventType, handler) {
297
+ var self = this;
298
+
299
+ this.bindDom(element, Hammer.EVENT_TYPES[eventType], function bindDomOnTouch(ev) {
300
+ var sourceEventType = ev.type.toLowerCase();
301
+
302
+ // onmouseup, but when touchend has been fired we do nothing.
303
+ // this is for touchdevices which also fire a mouseup on touchend
304
+ if(sourceEventType.match(/mouse/) && touch_triggered) {
305
+ return;
306
+ }
307
+
308
+ // mousebutton must be down or a touch event
309
+ else if( sourceEventType.match(/touch/) || // touch events are always on screen
310
+ sourceEventType.match(/pointerdown/) || // pointerevents touch
311
+ (sourceEventType.match(/mouse/) && ev.which === 1) // mouse is pressed
312
+ ){
313
+ enable_detect = true;
314
+ }
315
+
316
+ // we are in a touch event, set the touch triggered bool to true,
317
+ // this for the conflicts that may occur on ios and android
318
+ if(sourceEventType.match(/touch|pointer/)) {
319
+ touch_triggered = true;
320
+ }
321
+
322
+ // count the total touches on the screen
323
+ var count_touches = 0;
324
+
325
+ // when touch has been triggered in this detection session
326
+ // and we are now handling a mouse event, we stop that to prevent conflicts
327
+ if(enable_detect) {
328
+ // update pointerevent
329
+ if(Hammer.HAS_POINTEREVENTS && eventType != Hammer.EVENT_END) {
330
+ count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
331
+ }
332
+ // touch
333
+ else if(sourceEventType.match(/touch/)) {
334
+ count_touches = ev.touches.length;
335
+ }
336
+ // mouse
337
+ else if(!touch_triggered) {
338
+ count_touches = sourceEventType.match(/up/) ? 0 : 1;
339
+ }
340
+
341
+ // if we are in a end event, but when we remove one touch and
342
+ // we still have enough, set eventType to move
343
+ if(count_touches > 0 && eventType == Hammer.EVENT_END) {
344
+ eventType = Hammer.EVENT_MOVE;
345
+ }
346
+ // no touches, force the end event
347
+ else if(!count_touches) {
348
+ eventType = Hammer.EVENT_END;
349
+ }
350
+
351
+ // because touchend has no touches, and we often want to use these in our gestures,
352
+ // we send the last move event as our eventData in touchend
353
+ if(!count_touches && last_move_event !== null) {
354
+ ev = last_move_event;
355
+ }
356
+ // store the last move event
357
+ else {
358
+ last_move_event = ev;
359
+ }
360
+
361
+ // trigger the handler
362
+ handler.call(Hammer.detection, self.collectEventData(element, eventType, ev));
363
+
364
+ // remove pointerevent from list
365
+ if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) {
366
+ count_touches = Hammer.PointerEvent.updatePointer(eventType, ev);
367
+ }
368
+ }
369
+
370
+ //debug(sourceEventType +" "+ eventType);
371
+
372
+ // on the end we reset everything
373
+ if(!count_touches) {
374
+ last_move_event = null;
375
+ enable_detect = false;
376
+ touch_triggered = false;
377
+ Hammer.PointerEvent.reset();
378
+ }
379
+ });
380
+ },
381
+
382
+
383
+ /**
384
+ * we have different events for each device/browser
385
+ * determine what we need and set them in the Hammer.EVENT_TYPES constant
386
+ */
387
+ determineEventTypes: function determineEventTypes() {
388
+ // determine the eventtype we want to set
389
+ var types;
390
+
391
+ // pointerEvents magic
392
+ if(Hammer.HAS_POINTEREVENTS) {
393
+ types = Hammer.PointerEvent.getEvents();
394
+ }
395
+ // on Android, iOS, blackberry, windows mobile we dont want any mouseevents
396
+ else if(Hammer.NO_MOUSEEVENTS) {
397
+ types = [
398
+ 'touchstart',
399
+ 'touchmove',
400
+ 'touchend touchcancel'];
401
+ }
402
+ // for non pointer events browsers and mixed browsers,
403
+ // like chrome on windows8 touch laptop
404
+ else {
405
+ types = [
406
+ 'touchstart mousedown',
407
+ 'touchmove mousemove',
408
+ 'touchend touchcancel mouseup'];
409
+ }
410
+
411
+ Hammer.EVENT_TYPES[Hammer.EVENT_START] = types[0];
412
+ Hammer.EVENT_TYPES[Hammer.EVENT_MOVE] = types[1];
413
+ Hammer.EVENT_TYPES[Hammer.EVENT_END] = types[2];
414
+ },
415
+
416
+
417
+ /**
418
+ * create touchlist depending on the event
419
+ * @param {Object} ev
420
+ * @param {String} eventType used by the fakemultitouch plugin
421
+ */
422
+ getTouchList: function getTouchList(ev/*, eventType*/) {
423
+ // get the fake pointerEvent touchlist
424
+ if(Hammer.HAS_POINTEREVENTS) {
425
+ return Hammer.PointerEvent.getTouchList();
426
+ }
427
+ // get the touchlist
428
+ else if(ev.touches) {
429
+ return ev.touches;
430
+ }
431
+ // make fake touchlist from mouse position
432
+ else {
433
+ return [{
434
+ identifier: 1,
435
+ pageX: ev.pageX,
436
+ pageY: ev.pageY,
437
+ target: ev.target
438
+ }];
439
+ }
440
+ },
441
+
442
+
443
+ /**
444
+ * collect event data for Hammer js
445
+ * @param {HTMLElement} element
446
+ * @param {String} eventType like Hammer.EVENT_MOVE
447
+ * @param {Object} eventData
448
+ */
449
+ collectEventData: function collectEventData(element, eventType, ev) {
450
+ var touches = this.getTouchList(ev, eventType);
451
+
452
+ // find out pointerType
453
+ var pointerType = Hammer.POINTER_TOUCH;
454
+ if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) {
455
+ pointerType = Hammer.POINTER_MOUSE;
456
+ }
457
+
458
+ return {
459
+ center : Hammer.utils.getCenter(touches),
460
+ timeStamp : new Date().getTime(),
461
+ target : ev.target,
462
+ touches : touches,
463
+ eventType : eventType,
464
+ pointerType : pointerType,
465
+ srcEvent : ev,
466
+
467
+ /**
468
+ * prevent the browser default actions
469
+ * mostly used to disable scrolling of the browser
470
+ */
471
+ preventDefault: function() {
472
+ if(this.srcEvent.preventManipulation) {
473
+ this.srcEvent.preventManipulation();
474
+ }
475
+
476
+ if(this.srcEvent.preventDefault) {
477
+ this.srcEvent.preventDefault();
478
+ }
479
+ },
480
+
481
+ /**
482
+ * stop bubbling the event up to its parents
483
+ */
484
+ stopPropagation: function() {
485
+ this.srcEvent.stopPropagation();
486
+ },
487
+
488
+ /**
489
+ * immediately stop gesture detection
490
+ * might be useful after a swipe was detected
491
+ * @return {*}
492
+ */
493
+ stopDetect: function() {
494
+ return Hammer.detection.stopDetect();
495
+ }
496
+ };
497
+ }
498
+ };
499
+
500
+ Hammer.PointerEvent = {
501
+ /**
502
+ * holds all pointers
503
+ * @type {Object}
504
+ */
505
+ pointers: {},
506
+
507
+ /**
508
+ * get a list of pointers
509
+ * @returns {Array} touchlist
510
+ */
511
+ getTouchList: function() {
512
+ var self = this;
513
+ var touchlist = [];
514
+
515
+ // we can use forEach since pointerEvents only is in IE10
516
+ Object.keys(self.pointers).sort().forEach(function(id) {
517
+ touchlist.push(self.pointers[id]);
518
+ });
519
+ return touchlist;
520
+ },
521
+
522
+ /**
523
+ * update the position of a pointer
524
+ * @param {String} type Hammer.EVENT_END
525
+ * @param {Object} pointerEvent
526
+ */
527
+ updatePointer: function(type, pointerEvent) {
528
+ if(type == Hammer.EVENT_END) {
529
+ this.pointers = {};
530
+ }
531
+ else {
532
+ pointerEvent.identifier = pointerEvent.pointerId;
533
+ this.pointers[pointerEvent.pointerId] = pointerEvent;
534
+ }
535
+
536
+ return Object.keys(this.pointers).length;
537
+ },
538
+
539
+ /**
540
+ * check if ev matches pointertype
541
+ * @param {String} pointerType Hammer.POINTER_MOUSE
542
+ * @param {PointerEvent} ev
543
+ */
544
+ matchType: function(pointerType, ev) {
545
+ if(!ev.pointerType) {
546
+ return false;
547
+ }
548
+
549
+ var types = {};
550
+ types[Hammer.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == Hammer.POINTER_MOUSE);
551
+ types[Hammer.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == Hammer.POINTER_TOUCH);
552
+ types[Hammer.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == Hammer.POINTER_PEN);
553
+ return types[pointerType];
554
+ },
555
+
556
+
557
+ /**
558
+ * get events
559
+ */
560
+ getEvents: function() {
561
+ return [
562
+ 'pointerdown MSPointerDown',
563
+ 'pointermove MSPointerMove',
564
+ 'pointerup pointercancel MSPointerUp MSPointerCancel'
565
+ ];
566
+ },
567
+
568
+ /**
569
+ * reset the list
570
+ */
571
+ reset: function() {
572
+ this.pointers = {};
573
+ }
574
+ };
575
+
576
+
577
+ Hammer.utils = {
578
+ /**
579
+ * extend method,
580
+ * also used for cloning when dest is an empty object
581
+ * @param {Object} dest
582
+ * @param {Object} src
583
+ * @parm {Boolean} merge do a merge
584
+ * @returns {Object} dest
585
+ */
586
+ extend: function extend(dest, src, merge) {
587
+ for (var key in src) {
588
+ if(dest[key] !== undefined && merge) {
589
+ continue;
590
+ }
591
+ dest[key] = src[key];
592
+ }
593
+ return dest;
594
+ },
595
+
596
+
597
+ /**
598
+ * find if a node is in the given parent
599
+ * used for event delegation tricks
600
+ * @param {HTMLElement} node
601
+ * @param {HTMLElement} parent
602
+ * @returns {boolean} has_parent
603
+ */
604
+ hasParent: function(node, parent) {
605
+ while(node){
606
+ if(node == parent) {
607
+ return true;
608
+ }
609
+ node = node.parentNode;
610
+ }
611
+ return false;
612
+ },
613
+
614
+
615
+ /**
616
+ * get the center of all the touches
617
+ * @param {Array} touches
618
+ * @returns {Object} center
619
+ */
620
+ getCenter: function getCenter(touches) {
621
+ var valuesX = [], valuesY = [];
622
+
623
+ for(var t= 0,len=touches.length; t<len; t++) {
624
+ valuesX.push(touches[t].pageX);
625
+ valuesY.push(touches[t].pageY);
626
+ }
627
+
628
+ return {
629
+ pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
630
+ pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
631
+ };
632
+ },
633
+
634
+
635
+ /**
636
+ * calculate the velocity between two points
637
+ * @param {Number} delta_time
638
+ * @param {Number} delta_x
639
+ * @param {Number} delta_y
640
+ * @returns {Object} velocity
641
+ */
642
+ getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
643
+ return {
644
+ x: Math.abs(delta_x / delta_time) || 0,
645
+ y: Math.abs(delta_y / delta_time) || 0
646
+ };
647
+ },
648
+
649
+
650
+ /**
651
+ * calculate the angle between two coordinates
652
+ * @param {Touch} touch1
653
+ * @param {Touch} touch2
654
+ * @returns {Number} angle
655
+ */
656
+ getAngle: function getAngle(touch1, touch2) {
657
+ var y = touch2.pageY - touch1.pageY,
658
+ x = touch2.pageX - touch1.pageX;
659
+ return Math.atan2(y, x) * 180 / Math.PI;
660
+ },
661
+
662
+
663
+ /**
664
+ * angle to direction define
665
+ * @param {Touch} touch1
666
+ * @param {Touch} touch2
667
+ * @returns {String} direction constant, like Hammer.DIRECTION_LEFT
668
+ */
669
+ getDirection: function getDirection(touch1, touch2) {
670
+ var x = Math.abs(touch1.pageX - touch2.pageX),
671
+ y = Math.abs(touch1.pageY - touch2.pageY);
672
+
673
+ if(x >= y) {
674
+ return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
675
+ }
676
+ else {
677
+ return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
678
+ }
679
+ },
680
+
681
+
682
+ /**
683
+ * calculate the distance between two touches
684
+ * @param {Touch} touch1
685
+ * @param {Touch} touch2
686
+ * @returns {Number} distance
687
+ */
688
+ getDistance: function getDistance(touch1, touch2) {
689
+ var x = touch2.pageX - touch1.pageX,
690
+ y = touch2.pageY - touch1.pageY;
691
+ return Math.sqrt((x*x) + (y*y));
692
+ },
693
+
694
+
695
+ /**
696
+ * calculate the scale factor between two touchLists (fingers)
697
+ * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
698
+ * @param {Array} start
699
+ * @param {Array} end
700
+ * @returns {Number} scale
701
+ */
702
+ getScale: function getScale(start, end) {
703
+ // need two fingers...
704
+ if(start.length >= 2 && end.length >= 2) {
705
+ return this.getDistance(end[0], end[1]) /
706
+ this.getDistance(start[0], start[1]);
707
+ }
708
+ return 1;
709
+ },
710
+
711
+
712
+ /**
713
+ * calculate the rotation degrees between two touchLists (fingers)
714
+ * @param {Array} start
715
+ * @param {Array} end
716
+ * @returns {Number} rotation
717
+ */
718
+ getRotation: function getRotation(start, end) {
719
+ // need two fingers
720
+ if(start.length >= 2 && end.length >= 2) {
721
+ return this.getAngle(end[1], end[0]) -
722
+ this.getAngle(start[1], start[0]);
723
+ }
724
+ return 0;
725
+ },
726
+
727
+
728
+ /**
729
+ * boolean if the direction is vertical
730
+ * @param {String} direction
731
+ * @returns {Boolean} is_vertical
732
+ */
733
+ isVertical: function isVertical(direction) {
734
+ return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
735
+ },
736
+
737
+
738
+ /**
739
+ * stop browser default behavior with css props
740
+ * @param {HtmlElement} element
741
+ * @param {Object} css_props
742
+ */
743
+ stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
744
+ var prop,
745
+ vendors = ['webkit','khtml','moz','ms','o',''];
746
+
747
+ if(!css_props || !element.style) {
748
+ return;
749
+ }
750
+
751
+ // with css properties for modern browsers
752
+ for(var i = 0; i < vendors.length; i++) {
753
+ for(var p in css_props) {
754
+ if(css_props.hasOwnProperty(p)) {
755
+ prop = p;
756
+
757
+ // vender prefix at the property
758
+ if(vendors[i]) {
759
+ prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
760
+ }
761
+
762
+ // set the style
763
+ element.style[prop] = css_props[p];
764
+ }
765
+ }
766
+ }
767
+
768
+ // also the disable onselectstart
769
+ if(css_props.userSelect == 'none') {
770
+ element.onselectstart = function() {
771
+ return false;
772
+ };
773
+ }
774
+ }
775
+ };
776
+
777
+ Hammer.detection = {
778
+ // contains all registred Hammer.gestures in the correct order
779
+ gestures: [],
780
+
781
+ // data of the current Hammer.gesture detection session
782
+ current: null,
783
+
784
+ // the previous Hammer.gesture session data
785
+ // is a full clone of the previous gesture.current object
786
+ previous: null,
787
+
788
+ // when this becomes true, no gestures are fired
789
+ stopped: false,
790
+
791
+
792
+ /**
793
+ * start Hammer.gesture detection
794
+ * @param {Hammer.Instance} inst
795
+ * @param {Object} eventData
796
+ */
797
+ startDetect: function startDetect(inst, eventData) {
798
+ // already busy with a Hammer.gesture detection on an element
799
+ if(this.current) {
800
+ return;
801
+ }
802
+
803
+ this.stopped = false;
804
+
805
+ this.current = {
806
+ inst : inst, // reference to HammerInstance we're working for
807
+ startEvent : Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc
808
+ lastEvent : false, // last eventData
809
+ name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
810
+ };
811
+
812
+ this.detect(eventData);
813
+ },
814
+
815
+
816
+ /**
817
+ * Hammer.gesture detection
818
+ * @param {Object} eventData
819
+ * @param {Object} eventData
820
+ */
821
+ detect: function detect(eventData) {
822
+ if(!this.current || this.stopped) {
823
+ return;
824
+ }
825
+
826
+ // extend event data with calculations about scale, distance etc
827
+ eventData = this.extendEventData(eventData);
828
+
829
+ // instance options
830
+ var inst_options = this.current.inst.options;
831
+
832
+ // call Hammer.gesture handlers
833
+ for(var g=0,len=this.gestures.length; g<len; g++) {
834
+ var gesture = this.gestures[g];
835
+
836
+ // only when the instance options have enabled this gesture
837
+ if(!this.stopped && inst_options[gesture.name] !== false) {
838
+ // if a handler returns false, we stop with the detection
839
+ if(gesture.handler.call(gesture, eventData, this.current.inst) === false) {
840
+ this.stopDetect();
841
+ break;
842
+ }
843
+ }
844
+ }
845
+
846
+ // store as previous event event
847
+ if(this.current) {
848
+ this.current.lastEvent = eventData;
849
+ }
850
+
851
+ // endevent, but not the last touch, so dont stop
852
+ if(eventData.eventType == Hammer.EVENT_END && !eventData.touches.length-1) {
853
+ this.stopDetect();
854
+ }
855
+
856
+ return eventData;
857
+ },
858
+
859
+
860
+ /**
861
+ * clear the Hammer.gesture vars
862
+ * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
863
+ * to stop other Hammer.gestures from being fired
864
+ */
865
+ stopDetect: function stopDetect() {
866
+ // clone current data to the store as the previous gesture
867
+ // used for the double tap gesture, since this is an other gesture detect session
868
+ this.previous = Hammer.utils.extend({}, this.current);
869
+
870
+ // reset the current
871
+ this.current = null;
872
+
873
+ // stopped!
874
+ this.stopped = true;
875
+ },
876
+
877
+
878
+ /**
879
+ * extend eventData for Hammer.gestures
880
+ * @param {Object} ev
881
+ * @returns {Object} ev
882
+ */
883
+ extendEventData: function extendEventData(ev) {
884
+ var startEv = this.current.startEvent;
885
+
886
+ // if the touches change, set the new touches over the startEvent touches
887
+ // this because touchevents don't have all the touches on touchstart, or the
888
+ // user must place his fingers at the EXACT same time on the screen, which is not realistic
889
+ // but, sometimes it happens that both fingers are touching at the EXACT same time
890
+ if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) {
891
+ // extend 1 level deep to get the touchlist with the touch objects
892
+ startEv.touches = [];
893
+ for(var i=0,len=ev.touches.length; i<len; i++) {
894
+ startEv.touches.push(Hammer.utils.extend({}, ev.touches[i]));
895
+ }
896
+ }
897
+
898
+ var delta_time = ev.timeStamp - startEv.timeStamp,
899
+ delta_x = ev.center.pageX - startEv.center.pageX,
900
+ delta_y = ev.center.pageY - startEv.center.pageY,
901
+ velocity = Hammer.utils.getVelocity(delta_time, delta_x, delta_y);
902
+
903
+ Hammer.utils.extend(ev, {
904
+ deltaTime : delta_time,
905
+
906
+ deltaX : delta_x,
907
+ deltaY : delta_y,
908
+
909
+ velocityX : velocity.x,
910
+ velocityY : velocity.y,
911
+
912
+ distance : Hammer.utils.getDistance(startEv.center, ev.center),
913
+ angle : Hammer.utils.getAngle(startEv.center, ev.center),
914
+ direction : Hammer.utils.getDirection(startEv.center, ev.center),
915
+
916
+ scale : Hammer.utils.getScale(startEv.touches, ev.touches),
917
+ rotation : Hammer.utils.getRotation(startEv.touches, ev.touches),
918
+
919
+ startEvent : startEv
920
+ });
921
+
922
+ return ev;
923
+ },
924
+
925
+
926
+ /**
927
+ * register new gesture
928
+ * @param {Object} gesture object, see gestures.js for documentation
929
+ * @returns {Array} gestures
930
+ */
931
+ register: function register(gesture) {
932
+ // add an enable gesture options if there is no given
933
+ var options = gesture.defaults || {};
934
+ if(options[gesture.name] === undefined) {
935
+ options[gesture.name] = true;
936
+ }
937
+
938
+ // extend Hammer default options with the Hammer.gesture options
939
+ Hammer.utils.extend(Hammer.defaults, options, true);
940
+
941
+ // set its index
942
+ gesture.index = gesture.index || 1000;
943
+
944
+ // add Hammer.gesture to the list
945
+ this.gestures.push(gesture);
946
+
947
+ // sort the list by index
948
+ this.gestures.sort(function(a, b) {
949
+ if (a.index < b.index) {
950
+ return -1;
951
+ }
952
+ if (a.index > b.index) {
953
+ return 1;
954
+ }
955
+ return 0;
956
+ });
957
+
958
+ return this.gestures;
959
+ }
960
+ };
961
+
962
+
963
+ Hammer.gestures = Hammer.gestures || {};
964
+
965
+ /**
966
+ * Custom gestures
967
+ * ==============================
968
+ *
969
+ * Gesture object
970
+ * --------------------
971
+ * The object structure of a gesture:
972
+ *
973
+ * { name: 'mygesture',
974
+ * index: 1337,
975
+ * defaults: {
976
+ * mygesture_option: true
977
+ * }
978
+ * handler: function(type, ev, inst) {
979
+ * // trigger gesture event
980
+ * inst.trigger(this.name, ev);
981
+ * }
982
+ * }
983
+
984
+ * @param {String} name
985
+ * this should be the name of the gesture, lowercase
986
+ * it is also being used to disable/enable the gesture per instance config.
987
+ *
988
+ * @param {Number} [index=1000]
989
+ * the index of the gesture, where it is going to be in the stack of gestures detection
990
+ * like when you build an gesture that depends on the drag gesture, it is a good
991
+ * idea to place it after the index of the drag gesture.
992
+ *
993
+ * @param {Object} [defaults={}]
994
+ * the default settings of the gesture. these are added to the instance settings,
995
+ * and can be overruled per instance. you can also add the name of the gesture,
996
+ * but this is also added by default (and set to true).
997
+ *
998
+ * @param {Function} handler
999
+ * this handles the gesture detection of your custom gesture and receives the
1000
+ * following arguments:
1001
+ *
1002
+ * @param {Object} eventData
1003
+ * event data containing the following properties:
1004
+ * timeStamp {Number} time the event occurred
1005
+ * target {HTMLElement} target element
1006
+ * touches {Array} touches (fingers, pointers, mouse) on the screen
1007
+ * pointerType {String} kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH
1008
+ * center {Object} center position of the touches. contains pageX and pageY
1009
+ * deltaTime {Number} the total time of the touches in the screen
1010
+ * deltaX {Number} the delta on x axis we haved moved
1011
+ * deltaY {Number} the delta on y axis we haved moved
1012
+ * velocityX {Number} the velocity on the x
1013
+ * velocityY {Number} the velocity on y
1014
+ * angle {Number} the angle we are moving
1015
+ * direction {String} the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT
1016
+ * distance {Number} the distance we haved moved
1017
+ * scale {Number} scaling of the touches, needs 2 touches
1018
+ * rotation {Number} rotation of the touches, needs 2 touches *
1019
+ * eventType {String} matches Hammer.EVENT_START|MOVE|END
1020
+ * srcEvent {Object} the source event, like TouchStart or MouseDown *
1021
+ * startEvent {Object} contains the same properties as above,
1022
+ * but from the first touch. this is used to calculate
1023
+ * distances, deltaTime, scaling etc
1024
+ *
1025
+ * @param {Hammer.Instance} inst
1026
+ * the instance we are doing the detection for. you can get the options from
1027
+ * the inst.options object and trigger the gesture event by calling inst.trigger
1028
+ *
1029
+ *
1030
+ * Handle gestures
1031
+ * --------------------
1032
+ * inside the handler you can get/set Hammer.detection.current. This is the current
1033
+ * detection session. It has the following properties
1034
+ * @param {String} name
1035
+ * contains the name of the gesture we have detected. it has not a real function,
1036
+ * only to check in other gestures if something is detected.
1037
+ * like in the drag gesture we set it to 'drag' and in the swipe gesture we can
1038
+ * check if the current gesture is 'drag' by accessing Hammer.detection.current.name
1039
+ *
1040
+ * @readonly
1041
+ * @param {Hammer.Instance} inst
1042
+ * the instance we do the detection for
1043
+ *
1044
+ * @readonly
1045
+ * @param {Object} startEvent
1046
+ * contains the properties of the first gesture detection in this session.
1047
+ * Used for calculations about timing, distance, etc.
1048
+ *
1049
+ * @readonly
1050
+ * @param {Object} lastEvent
1051
+ * contains all the properties of the last gesture detect in this session.
1052
+ *
1053
+ * after the gesture detection session has been completed (user has released the screen)
1054
+ * the Hammer.detection.current object is copied into Hammer.detection.previous,
1055
+ * this is usefull for gestures like doubletap, where you need to know if the
1056
+ * previous gesture was a tap
1057
+ *
1058
+ * options that have been set by the instance can be received by calling inst.options
1059
+ *
1060
+ * You can trigger a gesture event by calling inst.trigger("mygesture", event).
1061
+ * The first param is the name of your gesture, the second the event argument
1062
+ *
1063
+ *
1064
+ * Register gestures
1065
+ * --------------------
1066
+ * When an gesture is added to the Hammer.gestures object, it is auto registered
1067
+ * at the setup of the first Hammer instance. You can also call Hammer.detection.register
1068
+ * manually and pass your gesture object as a param
1069
+ *
1070
+ */
1071
+
1072
+ /**
1073
+ * Hold
1074
+ * Touch stays at the same place for x time
1075
+ * @events hold
1076
+ */
1077
+ Hammer.gestures.Hold = {
1078
+ name: 'hold',
1079
+ index: 10,
1080
+ defaults: {
1081
+ hold_timeout : 500,
1082
+ hold_threshold : 1
1083
+ },
1084
+ timer: null,
1085
+ handler: function holdGesture(ev, inst) {
1086
+ switch(ev.eventType) {
1087
+ case Hammer.EVENT_START:
1088
+ // clear any running timers
1089
+ clearTimeout(this.timer);
1090
+
1091
+ // set the gesture so we can check in the timeout if it still is
1092
+ Hammer.detection.current.name = this.name;
1093
+
1094
+ // set timer and if after the timeout it still is hold,
1095
+ // we trigger the hold event
1096
+ this.timer = setTimeout(function() {
1097
+ if(Hammer.detection.current.name == 'hold') {
1098
+ inst.trigger('hold', ev);
1099
+ }
1100
+ }, inst.options.hold_timeout);
1101
+ break;
1102
+
1103
+ // when you move or end we clear the timer
1104
+ case Hammer.EVENT_MOVE:
1105
+ if(ev.distance > inst.options.hold_threshold) {
1106
+ clearTimeout(this.timer);
1107
+ }
1108
+ break;
1109
+
1110
+ case Hammer.EVENT_END:
1111
+ clearTimeout(this.timer);
1112
+ break;
1113
+ }
1114
+ }
1115
+ };
1116
+
1117
+
1118
+ /**
1119
+ * Tap/DoubleTap
1120
+ * Quick touch at a place or double at the same place
1121
+ * @events tap, doubletap
1122
+ */
1123
+ Hammer.gestures.Tap = {
1124
+ name: 'tap',
1125
+ index: 100,
1126
+ defaults: {
1127
+ tap_max_touchtime : 250,
1128
+ tap_max_distance : 10,
1129
+ tap_always : true,
1130
+ doubletap_distance : 20,
1131
+ doubletap_interval : 300
1132
+ },
1133
+ handler: function tapGesture(ev, inst) {
1134
+ if(ev.eventType == Hammer.EVENT_END) {
1135
+ // previous gesture, for the double tap since these are two different gesture detections
1136
+ var prev = Hammer.detection.previous,
1137
+ did_doubletap = false;
1138
+
1139
+ // when the touchtime is higher then the max touch time
1140
+ // or when the moving distance is too much
1141
+ if(ev.deltaTime > inst.options.tap_max_touchtime ||
1142
+ ev.distance > inst.options.tap_max_distance) {
1143
+ return;
1144
+ }
1145
+
1146
+ // check if double tap
1147
+ if(prev && prev.name == 'tap' &&
1148
+ (ev.timeStamp - prev.lastEvent.timeStamp) < inst.options.doubletap_interval &&
1149
+ ev.distance < inst.options.doubletap_distance) {
1150
+ inst.trigger('doubletap', ev);
1151
+ did_doubletap = true;
1152
+ }
1153
+
1154
+ // do a single tap
1155
+ if(!did_doubletap || inst.options.tap_always) {
1156
+ Hammer.detection.current.name = 'tap';
1157
+ inst.trigger(Hammer.detection.current.name, ev);
1158
+ }
1159
+ }
1160
+ }
1161
+ };
1162
+
1163
+
1164
+ /**
1165
+ * Swipe
1166
+ * triggers swipe events when the end velocity is above the threshold
1167
+ * @events swipe, swipeleft, swiperight, swipeup, swipedown
1168
+ */
1169
+ Hammer.gestures.Swipe = {
1170
+ name: 'swipe',
1171
+ index: 40,
1172
+ defaults: {
1173
+ // set 0 for unlimited, but this can conflict with transform
1174
+ swipe_max_touches : 1,
1175
+ swipe_velocity : 0.7
1176
+ },
1177
+ handler: function swipeGesture(ev, inst) {
1178
+ if(ev.eventType == Hammer.EVENT_END) {
1179
+ // max touches
1180
+ if(inst.options.swipe_max_touches > 0 &&
1181
+ ev.touches.length > inst.options.swipe_max_touches) {
1182
+ return;
1183
+ }
1184
+
1185
+ // when the distance we moved is too small we skip this gesture
1186
+ // or we can be already in dragging
1187
+ if(ev.velocityX > inst.options.swipe_velocity ||
1188
+ ev.velocityY > inst.options.swipe_velocity) {
1189
+ // trigger swipe events
1190
+ inst.trigger(this.name, ev);
1191
+ inst.trigger(this.name + ev.direction, ev);
1192
+ }
1193
+ }
1194
+ }
1195
+ };
1196
+
1197
+
1198
+ /**
1199
+ * Drag
1200
+ * Move with x fingers (default 1) around on the page. Blocking the scrolling when
1201
+ * moving left and right is a good practice. When all the drag events are blocking
1202
+ * you disable scrolling on that area.
1203
+ * @events drag, drapleft, dragright, dragup, dragdown
1204
+ */
1205
+ Hammer.gestures.Drag = {
1206
+ name: 'drag',
1207
+ index: 50,
1208
+ defaults: {
1209
+ drag_min_distance : 10,
1210
+ // set 0 for unlimited, but this can conflict with transform
1211
+ drag_max_touches : 1,
1212
+ // prevent default browser behavior when dragging occurs
1213
+ // be careful with it, it makes the element a blocking element
1214
+ // when you are using the drag gesture, it is a good practice to set this true
1215
+ drag_block_horizontal : false,
1216
+ drag_block_vertical : false,
1217
+ // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
1218
+ // It disallows vertical directions if the initial direction was horizontal, and vice versa.
1219
+ drag_lock_to_axis : false,
1220
+ // drag lock only kicks in when distance > drag_lock_min_distance
1221
+ // This way, locking occurs only when the distance has become large enough to reliably determine the direction
1222
+ drag_lock_min_distance : 25
1223
+ },
1224
+ triggered: false,
1225
+ handler: function dragGesture(ev, inst) {
1226
+ // current gesture isnt drag, but dragged is true
1227
+ // this means an other gesture is busy. now call dragend
1228
+ if(Hammer.detection.current.name != this.name && this.triggered) {
1229
+ inst.trigger(this.name +'end', ev);
1230
+ this.triggered = false;
1231
+ return;
1232
+ }
1233
+
1234
+ // max touches
1235
+ if(inst.options.drag_max_touches > 0 &&
1236
+ ev.touches.length > inst.options.drag_max_touches) {
1237
+ return;
1238
+ }
1239
+
1240
+ switch(ev.eventType) {
1241
+ case Hammer.EVENT_START:
1242
+ this.triggered = false;
1243
+ break;
1244
+
1245
+ case Hammer.EVENT_MOVE:
1246
+ // when the distance we moved is too small we skip this gesture
1247
+ // or we can be already in dragging
1248
+ if(ev.distance < inst.options.drag_min_distance &&
1249
+ Hammer.detection.current.name != this.name) {
1250
+ return;
1251
+ }
1252
+
1253
+ // we are dragging!
1254
+ Hammer.detection.current.name = this.name;
1255
+
1256
+ // lock drag to axis?
1257
+ if(Hammer.detection.current.lastEvent.drag_locked_to_axis || (inst.options.drag_lock_to_axis && inst.options.drag_lock_min_distance<=ev.distance)) {
1258
+ ev.drag_locked_to_axis = true;
1259
+ }
1260
+ var last_direction = Hammer.detection.current.lastEvent.direction;
1261
+ if(ev.drag_locked_to_axis && last_direction !== ev.direction) {
1262
+ // keep direction on the axis that the drag gesture started on
1263
+ if(Hammer.utils.isVertical(last_direction)) {
1264
+ ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
1265
+ }
1266
+ else {
1267
+ ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
1268
+ }
1269
+ }
1270
+
1271
+ // first time, trigger dragstart event
1272
+ if(!this.triggered) {
1273
+ inst.trigger(this.name +'start', ev);
1274
+ this.triggered = true;
1275
+ }
1276
+
1277
+ // trigger normal event
1278
+ inst.trigger(this.name, ev);
1279
+
1280
+ // direction event, like dragdown
1281
+ inst.trigger(this.name + ev.direction, ev);
1282
+
1283
+ // block the browser events
1284
+ if( (inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) ||
1285
+ (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) {
1286
+ ev.preventDefault();
1287
+ }
1288
+ break;
1289
+
1290
+ case Hammer.EVENT_END:
1291
+ // trigger dragend
1292
+ if(this.triggered) {
1293
+ inst.trigger(this.name +'end', ev);
1294
+ }
1295
+
1296
+ this.triggered = false;
1297
+ break;
1298
+ }
1299
+ }
1300
+ };
1301
+
1302
+
1303
+ /**
1304
+ * Transform
1305
+ * User want to scale or rotate with 2 fingers
1306
+ * @events transform, pinch, pinchin, pinchout, rotate
1307
+ */
1308
+ Hammer.gestures.Transform = {
1309
+ name: 'transform',
1310
+ index: 45,
1311
+ defaults: {
1312
+ // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
1313
+ transform_min_scale : 0.01,
1314
+ // rotation in degrees
1315
+ transform_min_rotation : 1,
1316
+ // prevent default browser behavior when two touches are on the screen
1317
+ // but it makes the element a blocking element
1318
+ // when you are using the transform gesture, it is a good practice to set this true
1319
+ transform_always_block : false
1320
+ },
1321
+ triggered: false,
1322
+ handler: function transformGesture(ev, inst) {
1323
+ // current gesture isnt drag, but dragged is true
1324
+ // this means an other gesture is busy. now call dragend
1325
+ if(Hammer.detection.current.name != this.name && this.triggered) {
1326
+ inst.trigger(this.name +'end', ev);
1327
+ this.triggered = false;
1328
+ return;
1329
+ }
1330
+
1331
+ // atleast multitouch
1332
+ if(ev.touches.length < 2) {
1333
+ return;
1334
+ }
1335
+
1336
+ // prevent default when two fingers are on the screen
1337
+ if(inst.options.transform_always_block) {
1338
+ ev.preventDefault();
1339
+ }
1340
+
1341
+ switch(ev.eventType) {
1342
+ case Hammer.EVENT_START:
1343
+ this.triggered = false;
1344
+ break;
1345
+
1346
+ case Hammer.EVENT_MOVE:
1347
+ var scale_threshold = Math.abs(1-ev.scale);
1348
+ var rotation_threshold = Math.abs(ev.rotation);
1349
+
1350
+ // when the distance we moved is too small we skip this gesture
1351
+ // or we can be already in dragging
1352
+ if(scale_threshold < inst.options.transform_min_scale &&
1353
+ rotation_threshold < inst.options.transform_min_rotation) {
1354
+ return;
1355
+ }
1356
+
1357
+ // we are transforming!
1358
+ Hammer.detection.current.name = this.name;
1359
+
1360
+ // first time, trigger dragstart event
1361
+ if(!this.triggered) {
1362
+ inst.trigger(this.name +'start', ev);
1363
+ this.triggered = true;
1364
+ }
1365
+
1366
+ inst.trigger(this.name, ev); // basic transform event
1367
+
1368
+ // trigger rotate event
1369
+ if(rotation_threshold > inst.options.transform_min_rotation) {
1370
+ inst.trigger('rotate', ev);
1371
+ }
1372
+
1373
+ // trigger pinch event
1374
+ if(scale_threshold > inst.options.transform_min_scale) {
1375
+ inst.trigger('pinch', ev);
1376
+ inst.trigger('pinch'+ ((ev.scale < 1) ? 'in' : 'out'), ev);
1377
+ }
1378
+ break;
1379
+
1380
+ case Hammer.EVENT_END:
1381
+ // trigger dragend
1382
+ if(this.triggered) {
1383
+ inst.trigger(this.name +'end', ev);
1384
+ }
1385
+
1386
+ this.triggered = false;
1387
+ break;
1388
+ }
1389
+ }
1390
+ };
1391
+
1392
+
1393
+ /**
1394
+ * Touch
1395
+ * Called as first, tells the user has touched the screen
1396
+ * @events touch
1397
+ */
1398
+ Hammer.gestures.Touch = {
1399
+ name: 'touch',
1400
+ index: -Infinity,
1401
+ defaults: {
1402
+ // call preventDefault at touchstart, and makes the element blocking by
1403
+ // disabling the scrolling of the page, but it improves gestures like
1404
+ // transforming and dragging.
1405
+ // be careful with using this, it can be very annoying for users to be stuck
1406
+ // on the page
1407
+ prevent_default: false,
1408
+
1409
+ // disable mouse events, so only touch (or pen!) input triggers events
1410
+ prevent_mouseevents: false
1411
+ },
1412
+ handler: function touchGesture(ev, inst) {
1413
+ if(inst.options.prevent_mouseevents && ev.pointerType == Hammer.POINTER_MOUSE) {
1414
+ ev.stopDetect();
1415
+ return;
1416
+ }
1417
+
1418
+ if(inst.options.prevent_default) {
1419
+ ev.preventDefault();
1420
+ }
1421
+
1422
+ if(ev.eventType == Hammer.EVENT_START) {
1423
+ inst.trigger(this.name, ev);
1424
+ }
1425
+ }
1426
+ };
1427
+
1428
+
1429
+ /**
1430
+ * Release
1431
+ * Called as last, tells the user has released the screen
1432
+ * @events release
1433
+ */
1434
+ Hammer.gestures.Release = {
1435
+ name: 'release',
1436
+ index: Infinity,
1437
+ handler: function releaseGesture(ev, inst) {
1438
+ if(ev.eventType == Hammer.EVENT_END) {
1439
+ inst.trigger(this.name, ev);
1440
+ }
1441
+ }
1442
+ };
1443
+
1444
+ // node export
1445
+ if(typeof module === 'object' && typeof module.exports === 'object'){
1446
+ module.exports = Hammer;
1447
+ }
1448
+ // just window export
1449
+ else {
1450
+ window.Hammer = Hammer;
1451
+
1452
+ // requireJS module definition
1453
+ if(typeof window.define === 'function' && window.define.amd) {
1454
+ window.define('hammer', [], function() {
1455
+ return Hammer;
1456
+ });
1457
+ }
1458
+ }
1459
+ }) (JSC3D);