PageSourceSearch

https://www.edmondokpestcontrol.com/files/theme/plugins.js?1755312083

js edmondokpestcontrol.com collected 2026-09-30 20:26:42 UTC 67,465 bytes, 2,460 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.