PageSourceSearch

https://sjcoescience.org/files/theme/plugins.js

js sjcoescience.org collected 2026-09-24 12:08:51 UTC 76,020 bytes, 2,460 lines download raw bytes

vendor: 31,202 bytes, lines 1-1062
1
2/*! Hammer.JS - v2.0.4 - 2014-09-28
3 * https://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
10    var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
11    var TEST_ELEMENT = document.createElement('div');
12
13    var TYPE_FUNCTION = 'function';
14
15    var round = Math.round;
16    var abs = Math.abs;
17    var 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     */
26    function 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     */
39    function 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     */
53    function 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     */
83    function 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     */
102    function 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     */
112    function 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     */
131    function 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     */
144    function 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     */
157    function 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     */
167    function 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     */
179    function 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     */
192    function 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     */
208    function 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     */
217    function 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     */
228    function 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     */
248    function 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     */
259    function 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     */
292    function 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     */
313    var _uniqueId = 1;
314    function uniqueId() {
315        return _uniqueId++;
316    }
317
318    /**
319     * get the window object of an element
320     * @param {HTMLElement} element
321     * @returns {DocumentView|Window}
322     */
323    function getWindowForElement(element) {
324        var doc = element.ownerDocument;
325        return (doc.defaultView || doc.parentWindow);
326    }
327
328    var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
329
330    var SUPPORT_TOUCH = ('ontouchstart' in window);
331    var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
332    var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
333
334    var INPUT_TYPE_TOUCH = 'touch';
335    var INPUT_TYPE_PEN = 'pen';
336    var INPUT_TYPE_MOUSE = 'mouse';
337    var INPUT_TYPE_KINECT = 'kinect';
338
339    var COMPUTE_INTERVAL = 25;
340
341    var INPUT_START = 1;
342    var INPUT_MOVE = 2;
343    var INPUT_END = 4;
344    var INPUT_CANCEL = 8;
345
346    var DIRECTION_NONE = 1;
347    var DIRECTION_LEFT = 2;
348    var DIRECTION_RIGHT = 4;
349    var DIRECTION_UP = 8;
350    var DIRECTION_DOWN = 16;
351
352    var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
353    var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
354    var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
355
356    var PROPS_XY = ['x', 'y'];
357    var 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     */
366    function 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
385    Input.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     */
417    function 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     */
441    function 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     */
473    function 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
517    function 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     */
544    function 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     */
579    function 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     */
606    function 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     */
637    function 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     */
650    function 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     */
668    function 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     */
685    function 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     */
700    function 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
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     */
711    function getScale(start, end) {
712        return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
713    }
714
715    var MOUSE_INPUT_MAP = {
716        mousedown: INPUT_START,
717        mousemove: INPUT_MOVE,
718        mouseup: INPUT_END
719    };
720
721    var MOUSE_ELEMENT_EVENTS = 'mousedown';
722    var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
723
724    /**
725     * Mouse events input
726     * @constructor
727     * @extends Input
728     */
729    function 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
739    inherit(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
774    var 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
783    var 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
790    var POINTER_ELEMENT_EVENTS = 'pointerdown';
791    var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
792
793    // IE10 has prefixed support, and case-sensitive
794    if (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     */
804    function 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
813    inherit(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
863    var SINGLE_TOUCH_INPUT_MAP = {
864        touchstart: INPUT_START,
865        touchmove: INPUT_MOVE,
866        touchend: INPUT_END,
867        touchcancel: INPUT_CANCEL
868    };
869
870    var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
871    var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
872
873    /**
874     * Touch events input
875     * @constructor
876     * @extends Input
877     */
878    function 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
886    inherit(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     */
921    function 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
932    var TOUCH_INPUT_MAP = {
933        touchstart: INPUT_START,
934        touchmove: INPUT_MOVE,
935        touchend: INPUT_END,
936        touchcancel: INPUT_CANCEL
937    };
938
939    var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
940
941    /**
942     * Multi-user touch events input
943     * @constructor
944     * @extends Input
945     */
946    function TouchInput() {
947        this.evTarget = TOUCH_TARGET_EVENTS;
948        this.targetIds = {};
949
950        Input.apply(this, arguments);
951    }
952
953    inherit(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     */
976    function 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     */
1040    function 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
1048    inherit(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;
vendor: 11,512 bytes, lines 1063-1415
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
1084    var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
1085    var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
1086
1087    // magical touchAction value
1088    var TOUCH_ACTION_COMPUTE = 'compute';
1089    var TOUCH_ACTION_AUTO = 'auto';
1090    var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
1091    var TOUCH_ACTION_NONE = 'none';
1092    var TOUCH_ACTION_PAN_X = 'pan-x';
1093    var 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     */
1102    function TouchAction(manager, value) {
1103        this.manager = manager;
1104        this.set(value);
1105    }
1106
1107    TouchAction.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     */
1191    function 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     */
1245    var STATE_POSSIBLE = 1;
1246    var STATE_BEGAN = 2;
1247    var STATE_CHANGED = 4;
1248    var STATE_ENDED = 8;
1249    var STATE_RECOGNIZED = STATE_ENDED;
1250    var STATE_CANCELLED = 16;
1251    var STATE_FAILED = 32;
1252
1253    /**
1254     * Recognizer
1255     * Every recognizer needs to extend from this class.
1256     * @constructor
1257     * @param {Object} options
1258     */
1259    function 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
1274    Recognizer.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: 8,132 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     */
1496    function 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     */
1514    function 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     */
1533    function 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     */
1546    function AttrRecognizer() {
1547        Recognizer.apply(this, arguments);
1548    }
1549
1550    inherit(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     */
1608    function PanRecognizer() {
1609        AttrRecognizer.apply(this, arguments);
1610
1611        this.pX = null;
1612        this.pY = null;
1613    }
1614
1615    inherit(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
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     */
1687    function PinchRecognizer() {
1688        AttrRecognizer.apply(this, arguments);
1689    }
1690
1691    inherit(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
vendor: 6,348 bytes, lines 1722-1923
1722     * Recognized when the pointer is down for x ms without any movement.
1723     * @constructor
1724     * @extends Recognizer
1725     */
1726    function PressRecognizer() {
1727        Recognizer.apply(this, arguments);
1728
1729        this._timer = null;
1730        this._input = null;
1731    }
1732
1733    inherit(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
1793     * Recognized when two or more pointer are moving in a circular motion.
1794     * @constructor
1795     * @extends AttrRecognizer
1796     */
1797    function RotateRecognizer() {
1798        AttrRecognizer.apply(this, arguments);
1799    }
1800
1801    inherit(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     */
1828    function SwipeRecognizer() {
1829        AttrRecognizer.apply(this, arguments);
1830    }
1831
1832    inherit(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     */
1887    function 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
1900    inherit(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;
vendor: 11,801 bytes, lines 1924-2260
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
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     */
1998    function 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     */
2007    Hammer.VERSION = '2.0.4';
2008
2009    /**
2010     * default settings
2011     * @namespace
2012     */
2013    Hammer.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
2121    var STOP = 1;
2122    var FORCED_STOP = 2;
2123
2124    /**
2125     * Manager
2126     * @param {HTMLElement} element
2127     * @param {Object} [options]
2128     * @constructor
2129     */
2130    function 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
2153    Manager.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     */
2383    function 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     */
2395    function 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
2402    extend(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
2454    if (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.