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