PageSourceSearch

https://club-ihtiolog.ru/o-klube/shema-territorii/assets/hammer.js

js club-ihtiolog.ru collected 2026-10-05 23:01:03 UTC 78,529 bytes, 2,463 lines download raw bytes

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