source: main/trunk/model-interfaces-dev/whakatohea/iframe/Tīpuna-WMTB_files/plugins.js@ 33835

Last change on this file since 33835 was 33835, checked in by davidb, 4 years ago

Supporting iframe files now located within interface area

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