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