Revert "Fix Hammer.js, so it uses screen coords instead of client coords"
authorLanius Trolling <lanius@laniustrolling.dev>
Sun, 17 Dec 2023 15:02:01 +0000 (10:02 -0500)
committerLanius Trolling <lanius@laniustrolling.dev>
Sun, 17 Dec 2023 15:02:01 +0000 (10:02 -0500)
This reverts commit 4e75f1ed5a14c5859a6e88a97e46b873243c9ecc.

src/main/kotlin/info/mechyrdia/lore/view_map.kt
src/main/resources/static/obj-viewer/hammer.min.js [new file with mode: 0644]
src/main/resources/static/obj-viewer/hammer.modified.js [deleted file]

index 6b5af0137cd62e98d1ac262ae1a3a4b8c9a729ce..91ee00318a214ff68a7eab9ac7009693b6300608 100644 (file)
@@ -30,7 +30,7 @@ fun ApplicationCall.galaxyMapPage(): HTML.() -> Unit {
                        
                        script(src = "/static/obj-viewer/three.js") {}
                        script(src = "/static/obj-viewer/three-examples.js") {}
-                       script(src = "/static/obj-viewer/hammer.modified.js") {}
+                       script(src = "/static/obj-viewer/hammer.min.js") {}
                        
                        link(rel = "icon", type = "image/svg+xml", href = "/static/images/icon.png")
                        
diff --git a/src/main/resources/static/obj-viewer/hammer.min.js b/src/main/resources/static/obj-viewer/hammer.min.js
new file mode 100644 (file)
index 0000000..94b653d
--- /dev/null
@@ -0,0 +1,6 @@
+/*! Hammer.JS - v2.0.8 - 2016-04-23
+ * http://hammerjs.github.io/
+ *
+ * Copyright (c) 2016 Jorik Tangelder;
+ * Licensed under the MIT license */
+!function(a,b,c,d){"use strict";function e(a,b,c){return setTimeout(j(a,c),b)}function f(a,b,c){return Array.isArray(a)?(g(a,c[b],c),!0):!1}function g(a,b,c){var e;if(a)if(a.forEach)a.forEach(b,c);else if(a.length!==d)for(e=0;e<a.length;)b.call(c,a[e],e,a),e++;else for(e in a)a.hasOwnProperty(e)&&b.call(c,a[e],e,a)}function h(b,c,d){var e="DEPRECATED METHOD: "+c+"\n"+d+" AT \n";return function(){var c=new Error("get-stack-trace"),d=c&&c.stack?c.stack.replace(/^[^\(]+?[\n$]/gm,"").replace(/^\s+at\s+/gm,"").replace(/^Object.<anonymous>\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",f=a.console&&(a.console.warn||a.console.log);return f&&f.call(a.console,e,d),b.apply(this,arguments)}}function i(a,b,c){var d,e=b.prototype;d=a.prototype=Object.create(e),d.constructor=a,d._super=e,c&&la(d,c)}function j(a,b){return function(){return a.apply(b,arguments)}}function k(a,b){return typeof a==oa?a.apply(b?b[0]||d:d,b):a}function l(a,b){return a===d?b:a}function m(a,b,c){g(q(b),function(b){a.addEventListener(b,c,!1)})}function n(a,b,c){g(q(b),function(b){a.removeEventListener(b,c,!1)})}function o(a,b){for(;a;){if(a==b)return!0;a=a.parentNode}return!1}function p(a,b){return a.indexOf(b)>-1}function q(a){return a.trim().split(/\s+/g)}function r(a,b,c){if(a.indexOf&&!c)return a.indexOf(b);for(var d=0;d<a.length;){if(c&&a[d][c]==b||!c&&a[d]===b)return d;d++}return-1}function s(a){return Array.prototype.slice.call(a,0)}function t(a,b,c){for(var d=[],e=[],f=0;f<a.length;){var g=b?a[f][b]:a[f];r(e,g)<0&&d.push(a[f]),e[f]=g,f++}return c&&(d=b?d.sort(function(a,c){return a[b]>c[b]}):d.sort()),d}function u(a,b){for(var c,e,f=b[0].toUpperCase()+b.slice(1),g=0;g<ma.length;){if(c=ma[g],e=c?c+f:b,e in a)return e;g++}return d}function v(){return ua++}function w(b){var c=b.ownerDocument||b;return c.defaultView||c.parentWindow||a}function x(a,b){var c=this;this.manager=a,this.callback=b,this.element=a.element,this.target=a.options.inputTarget,this.domHandler=function(b){k(a.options.enable,[a])&&c.handler(b)},this.init()}function y(a){var b,c=a.options.inputClass;return new(b=c?c:xa?M:ya?P:wa?R:L)(a,z)}function z(a,b,c){var d=c.pointers.length,e=c.changedPointers.length,f=b&Ea&&d-e===0,g=b&(Ga|Ha)&&d-e===0;c.isFirst=!!f,c.isFinal=!!g,f&&(a.session={}),c.eventType=b,A(a,c),a.emit("hammer.input",c),a.recognize(c),a.session.prevInput=c}function A(a,b){var c=a.session,d=b.pointers,e=d.length;c.firstInput||(c.firstInput=D(b)),e>1&&!c.firstMultiple?c.firstMultiple=D(b):1===e&&(c.firstMultiple=!1);var f=c.firstInput,g=c.firstMultiple,h=g?g.center:f.center,i=b.center=E(d);b.timeStamp=ra(),b.deltaTime=b.timeStamp-f.timeStamp,b.angle=I(h,i),b.distance=H(h,i),B(c,b),b.offsetDirection=G(b.deltaX,b.deltaY);var j=F(b.deltaTime,b.deltaX,b.deltaY);b.overallVelocityX=j.x,b.overallVelocityY=j.y,b.overallVelocity=qa(j.x)>qa(j.y)?j.x:j.y,b.scale=g?K(g.pointers,d):1,b.rotation=g?J(g.pointers,d):0,b.maxPointers=c.prevInput?b.pointers.length>c.prevInput.maxPointers?b.pointers.length:c.prevInput.maxPointers:b.pointers.length,C(c,b);var k=a.element;o(b.srcEvent.target,k)&&(k=b.srcEvent.target),b.target=k}function B(a,b){var c=b.center,d=a.offsetDelta||{},e=a.prevDelta||{},f=a.prevInput||{};b.eventType!==Ea&&f.eventType!==Ga||(e=a.prevDelta={x:f.deltaX||0,y:f.deltaY||0},d=a.offsetDelta={x:c.x,y:c.y}),b.deltaX=e.x+(c.x-d.x),b.deltaY=e.y+(c.y-d.y)}function C(a,b){var c,e,f,g,h=a.lastInterval||b,i=b.timeStamp-h.timeStamp;if(b.eventType!=Ha&&(i>Da||h.velocity===d)){var j=b.deltaX-h.deltaX,k=b.deltaY-h.deltaY,l=F(i,j,k);e=l.x,f=l.y,c=qa(l.x)>qa(l.y)?l.x:l.y,g=G(j,k),a.lastInterval=b}else c=h.velocity,e=h.velocityX,f=h.velocityY,g=h.direction;b.velocity=c,b.velocityX=e,b.velocityY=f,b.direction=g}function D(a){for(var b=[],c=0;c<a.pointers.length;)b[c]={clientX:pa(a.pointers[c].clientX),clientY:pa(a.pointers[c].clientY)},c++;return{timeStamp:ra(),pointers:b,center:E(b),deltaX:a.deltaX,deltaY:a.deltaY}}function E(a){var b=a.length;if(1===b)return{x:pa(a[0].clientX),y:pa(a[0].clientY)};for(var c=0,d=0,e=0;b>e;)c+=a[e].clientX,d+=a[e].clientY,e++;return{x:pa(c/b),y:pa(d/b)}}function F(a,b,c){return{x:b/a||0,y:c/a||0}}function G(a,b){return a===b?Ia:qa(a)>=qa(b)?0>a?Ja:Ka:0>b?La:Ma}function H(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return Math.sqrt(d*d+e*e)}function I(a,b,c){c||(c=Qa);var d=b[c[0]]-a[c[0]],e=b[c[1]]-a[c[1]];return 180*Math.atan2(e,d)/Math.PI}function J(a,b){return I(b[1],b[0],Ra)+I(a[1],a[0],Ra)}function K(a,b){return H(b[0],b[1],Ra)/H(a[0],a[1],Ra)}function L(){this.evEl=Ta,this.evWin=Ua,this.pressed=!1,x.apply(this,arguments)}function M(){this.evEl=Xa,this.evWin=Ya,x.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}function N(){this.evTarget=$a,this.evWin=_a,this.started=!1,x.apply(this,arguments)}function O(a,b){var c=s(a.touches),d=s(a.changedTouches);return b&(Ga|Ha)&&(c=t(c.concat(d),"identifier",!0)),[c,d]}function P(){this.evTarget=bb,this.targetIds={},x.apply(this,arguments)}function Q(a,b){var c=s(a.touches),d=this.targetIds;if(b&(Ea|Fa)&&1===c.length)return d[c[0].identifier]=!0,[c,c];var e,f,g=s(a.changedTouches),h=[],i=this.target;if(f=c.filter(function(a){return o(a.target,i)}),b===Ea)for(e=0;e<f.length;)d[f[e].identifier]=!0,e++;for(e=0;e<g.length;)d[g[e].identifier]&&h.push(g[e]),b&(Ga|Ha)&&delete d[g[e].identifier],e++;return h.length?[t(f.concat(h),"identifier",!0),h]:void 0}function R(){x.apply(this,arguments);var a=j(this.handler,this);this.touch=new P(this.manager,a),this.mouse=new L(this.manager,a),this.primaryTouch=null,this.lastTouches=[]}function S(a,b){a&Ea?(this.primaryTouch=b.changedPointers[0].identifier,T.call(this,b)):a&(Ga|Ha)&&T.call(this,b)}function T(a){var b=a.changedPointers[0];if(b.identifier===this.primaryTouch){var c={x:b.clientX,y:b.clientY};this.lastTouches.push(c);var d=this.lastTouches,e=function(){var a=d.indexOf(c);a>-1&&d.splice(a,1)};setTimeout(e,cb)}}function U(a){for(var b=a.srcEvent.clientX,c=a.srcEvent.clientY,d=0;d<this.lastTouches.length;d++){var e=this.lastTouches[d],f=Math.abs(b-e.x),g=Math.abs(c-e.y);if(db>=f&&db>=g)return!0}return!1}function V(a,b){this.manager=a,this.set(b)}function W(a){if(p(a,jb))return jb;var b=p(a,kb),c=p(a,lb);return b&&c?jb:b||c?b?kb:lb:p(a,ib)?ib:hb}function X(){if(!fb)return!1;var b={},c=a.CSS&&a.CSS.supports;return["auto","manipulation","pan-y","pan-x","pan-x pan-y","none"].forEach(function(d){b[d]=c?a.CSS.supports("touch-action",d):!0}),b}function Y(a){this.options=la({},this.defaults,a||{}),this.id=v(),this.manager=null,this.options.enable=l(this.options.enable,!0),this.state=nb,this.simultaneous={},this.requireFail=[]}function Z(a){return a&sb?"cancel":a&qb?"end":a&pb?"move":a&ob?"start":""}function $(a){return a==Ma?"down":a==La?"up":a==Ja?"left":a==Ka?"right":""}function _(a,b){var c=b.manager;return c?c.get(a):a}function aa(){Y.apply(this,arguments)}function ba(){aa.apply(this,arguments),this.pX=null,this.pY=null}function ca(){aa.apply(this,arguments)}function da(){Y.apply(this,arguments),this._timer=null,this._input=null}function ea(){aa.apply(this,arguments)}function fa(){aa.apply(this,arguments)}function ga(){Y.apply(this,arguments),this.pTime=!1,this.pCenter=!1,this._timer=null,this._input=null,this.count=0}function ha(a,b){return b=b||{},b.recognizers=l(b.recognizers,ha.defaults.preset),new ia(a,b)}function ia(a,b){this.options=la({},ha.defaults,b||{}),this.options.inputTarget=this.options.inputTarget||a,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=a,this.input=y(this),this.touchAction=new V(this,this.options.touchAction),ja(this,!0),g(this.options.recognizers,function(a){var b=this.add(new a[0](a[1]));a[2]&&b.recognizeWith(a[2]),a[3]&&b.requireFailure(a[3])},this)}function ja(a,b){var c=a.element;if(c.style){var d;g(a.options.cssProps,function(e,f){d=u(c.style,f),b?(a.oldCssProps[d]=c.style[d],c.style[d]=e):c.style[d]=a.oldCssProps[d]||""}),b||(a.oldCssProps={})}}function ka(a,c){var d=b.createEvent("Event");d.initEvent(a,!0,!0),d.gesture=c,c.target.dispatchEvent(d)}var la,ma=["","webkit","Moz","MS","ms","o"],na=b.createElement("div"),oa="function",pa=Math.round,qa=Math.abs,ra=Date.now;la="function"!=typeof Object.assign?function(a){if(a===d||null===a)throw new TypeError("Cannot convert undefined or null to object");for(var b=Object(a),c=1;c<arguments.length;c++){var e=arguments[c];if(e!==d&&null!==e)for(var f in e)e.hasOwnProperty(f)&&(b[f]=e[f])}return b}:Object.assign;var sa=h(function(a,b,c){for(var e=Object.keys(b),f=0;f<e.length;)(!c||c&&a[e[f]]===d)&&(a[e[f]]=b[e[f]]),f++;return a},"extend","Use `assign`."),ta=h(function(a,b){return sa(a,b,!0)},"merge","Use `assign`."),ua=1,va=/mobile|tablet|ip(ad|hone|od)|android/i,wa="ontouchstart"in a,xa=u(a,"PointerEvent")!==d,ya=wa&&va.test(navigator.userAgent),za="touch",Aa="pen",Ba="mouse",Ca="kinect",Da=25,Ea=1,Fa=2,Ga=4,Ha=8,Ia=1,Ja=2,Ka=4,La=8,Ma=16,Na=Ja|Ka,Oa=La|Ma,Pa=Na|Oa,Qa=["x","y"],Ra=["clientX","clientY"];x.prototype={handler:function(){},init:function(){this.evEl&&m(this.element,this.evEl,this.domHandler),this.evTarget&&m(this.target,this.evTarget,this.domHandler),this.evWin&&m(w(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&n(this.element,this.evEl,this.domHandler),this.evTarget&&n(this.target,this.evTarget,this.domHandler),this.evWin&&n(w(this.element),this.evWin,this.domHandler)}};var Sa={mousedown:Ea,mousemove:Fa,mouseup:Ga},Ta="mousedown",Ua="mousemove mouseup";i(L,x,{handler:function(a){var b=Sa[a.type];b&Ea&&0===a.button&&(this.pressed=!0),b&Fa&&1!==a.which&&(b=Ga),this.pressed&&(b&Ga&&(this.pressed=!1),this.callback(this.manager,b,{pointers:[a],changedPointers:[a],pointerType:Ba,srcEvent:a}))}});var Va={pointerdown:Ea,pointermove:Fa,pointerup:Ga,pointercancel:Ha,pointerout:Ha},Wa={2:za,3:Aa,4:Ba,5:Ca},Xa="pointerdown",Ya="pointermove pointerup pointercancel";a.MSPointerEvent&&!a.PointerEvent&&(Xa="MSPointerDown",Ya="MSPointerMove MSPointerUp MSPointerCancel"),i(M,x,{handler:function(a){var b=this.store,c=!1,d=a.type.toLowerCase().replace("ms",""),e=Va[d],f=Wa[a.pointerType]||a.pointerType,g=f==za,h=r(b,a.pointerId,"pointerId");e&Ea&&(0===a.button||g)?0>h&&(b.push(a),h=b.length-1):e&(Ga|Ha)&&(c=!0),0>h||(b[h]=a,this.callback(this.manager,e,{pointers:b,changedPointers:[a],pointerType:f,srcEvent:a}),c&&b.splice(h,1))}});var Za={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},$a="touchstart",_a="touchstart touchmove touchend touchcancel";i(N,x,{handler:function(a){var b=Za[a.type];if(b===Ea&&(this.started=!0),this.started){var c=O.call(this,a,b);b&(Ga|Ha)&&c[0].length-c[1].length===0&&(this.started=!1),this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}}});var ab={touchstart:Ea,touchmove:Fa,touchend:Ga,touchcancel:Ha},bb="touchstart touchmove touchend touchcancel";i(P,x,{handler:function(a){var b=ab[a.type],c=Q.call(this,a,b);c&&this.callback(this.manager,b,{pointers:c[0],changedPointers:c[1],pointerType:za,srcEvent:a})}});var cb=2500,db=25;i(R,x,{handler:function(a,b,c){var d=c.pointerType==za,e=c.pointerType==Ba;if(!(e&&c.sourceCapabilities&&c.sourceCapabilities.firesTouchEvents)){if(d)S.call(this,b,c);else if(e&&U.call(this,c))return;this.callback(a,b,c)}},destroy:function(){this.touch.destroy(),this.mouse.destroy()}});var eb=u(na.style,"touchAction"),fb=eb!==d,gb="compute",hb="auto",ib="manipulation",jb="none",kb="pan-x",lb="pan-y",mb=X();V.prototype={set:function(a){a==gb&&(a=this.compute()),fb&&this.manager.element.style&&mb[a]&&(this.manager.element.style[eb]=a),this.actions=a.toLowerCase().trim()},update:function(){this.set(this.manager.options.touchAction)},compute:function(){var a=[];return g(this.manager.recognizers,function(b){k(b.options.enable,[b])&&(a=a.concat(b.getTouchAction()))}),W(a.join(" "))},preventDefaults:function(a){var b=a.srcEvent,c=a.offsetDirection;if(this.manager.session.prevented)return void b.preventDefault();var d=this.actions,e=p(d,jb)&&!mb[jb],f=p(d,lb)&&!mb[lb],g=p(d,kb)&&!mb[kb];if(e){var h=1===a.pointers.length,i=a.distance<2,j=a.deltaTime<250;if(h&&i&&j)return}return g&&f?void 0:e||f&&c&Na||g&&c&Oa?this.preventSrc(b):void 0},preventSrc:function(a){this.manager.session.prevented=!0,a.preventDefault()}};var nb=1,ob=2,pb=4,qb=8,rb=qb,sb=16,tb=32;Y.prototype={defaults:{},set:function(a){return la(this.options,a),this.manager&&this.manager.touchAction.update(),this},recognizeWith:function(a){if(f(a,"recognizeWith",this))return this;var b=this.simultaneous;return a=_(a,this),b[a.id]||(b[a.id]=a,a.recognizeWith(this)),this},dropRecognizeWith:function(a){return f(a,"dropRecognizeWith",this)?this:(a=_(a,this),delete this.simultaneous[a.id],this)},requireFailure:function(a){if(f(a,"requireFailure",this))return this;var b=this.requireFail;return a=_(a,this),-1===r(b,a)&&(b.push(a),a.requireFailure(this)),this},dropRequireFailure:function(a){if(f(a,"dropRequireFailure",this))return this;a=_(a,this);var b=r(this.requireFail,a);return b>-1&&this.requireFail.splice(b,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(a){return!!this.simultaneous[a.id]},emit:function(a){function b(b){c.manager.emit(b,a)}var c=this,d=this.state;qb>d&&b(c.options.event+Z(d)),b(c.options.event),a.additionalEvent&&b(a.additionalEvent),d>=qb&&b(c.options.event+Z(d))},tryEmit:function(a){return this.canEmit()?this.emit(a):void(this.state=tb)},canEmit:function(){for(var a=0;a<this.requireFail.length;){if(!(this.requireFail[a].state&(tb|nb)))return!1;a++}return!0},recognize:function(a){var b=la({},a);return k(this.options.enable,[this,b])?(this.state&(rb|sb|tb)&&(this.state=nb),this.state=this.process(b),void(this.state&(ob|pb|qb|sb)&&this.tryEmit(b))):(this.reset(),void(this.state=tb))},process:function(a){},getTouchAction:function(){},reset:function(){}},i(aa,Y,{defaults:{pointers:1},attrTest:function(a){var b=this.options.pointers;return 0===b||a.pointers.length===b},process:function(a){var b=this.state,c=a.eventType,d=b&(ob|pb),e=this.attrTest(a);return d&&(c&Ha||!e)?b|sb:d||e?c&Ga?b|qb:b&ob?b|pb:ob:tb}}),i(ba,aa,{defaults:{event:"pan",threshold:10,pointers:1,direction:Pa},getTouchAction:function(){var a=this.options.direction,b=[];return a&Na&&b.push(lb),a&Oa&&b.push(kb),b},directionTest:function(a){var b=this.options,c=!0,d=a.distance,e=a.direction,f=a.deltaX,g=a.deltaY;return e&b.direction||(b.direction&Na?(e=0===f?Ia:0>f?Ja:Ka,c=f!=this.pX,d=Math.abs(a.deltaX)):(e=0===g?Ia:0>g?La:Ma,c=g!=this.pY,d=Math.abs(a.deltaY))),a.direction=e,c&&d>b.threshold&&e&b.direction},attrTest:function(a){return aa.prototype.attrTest.call(this,a)&&(this.state&ob||!(this.state&ob)&&this.directionTest(a))},emit:function(a){this.pX=a.deltaX,this.pY=a.deltaY;var b=$(a.direction);b&&(a.additionalEvent=this.options.event+b),this._super.emit.call(this,a)}}),i(ca,aa,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.scale-1)>this.options.threshold||this.state&ob)},emit:function(a){if(1!==a.scale){var b=a.scale<1?"in":"out";a.additionalEvent=this.options.event+b}this._super.emit.call(this,a)}}),i(da,Y,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[hb]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime>b.time;if(this._input=a,!d||!c||a.eventType&(Ga|Ha)&&!f)this.reset();else if(a.eventType&Ea)this.reset(),this._timer=e(function(){this.state=rb,this.tryEmit()},b.time,this);else if(a.eventType&Ga)return rb;return tb},reset:function(){clearTimeout(this._timer)},emit:function(a){this.state===rb&&(a&&a.eventType&Ga?this.manager.emit(this.options.event+"up",a):(this._input.timeStamp=ra(),this.manager.emit(this.options.event,this._input)))}}),i(ea,aa,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[jb]},attrTest:function(a){return this._super.attrTest.call(this,a)&&(Math.abs(a.rotation)>this.options.threshold||this.state&ob)}}),i(fa,aa,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:Na|Oa,pointers:1},getTouchAction:function(){return ba.prototype.getTouchAction.call(this)},attrTest:function(a){var b,c=this.options.direction;return c&(Na|Oa)?b=a.overallVelocity:c&Na?b=a.overallVelocityX:c&Oa&&(b=a.overallVelocityY),this._super.attrTest.call(this,a)&&c&a.offsetDirection&&a.distance>this.options.threshold&&a.maxPointers==this.options.pointers&&qa(b)>this.options.velocity&&a.eventType&Ga},emit:function(a){var b=$(a.offsetDirection);b&&this.manager.emit(this.options.event+b,a),this.manager.emit(this.options.event,a)}}),i(ga,Y,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[ib]},process:function(a){var b=this.options,c=a.pointers.length===b.pointers,d=a.distance<b.threshold,f=a.deltaTime<b.time;if(this.reset(),a.eventType&Ea&&0===this.count)return this.failTimeout();if(d&&f&&c){if(a.eventType!=Ga)return this.failTimeout();var g=this.pTime?a.timeStamp-this.pTime<b.interval:!0,h=!this.pCenter||H(this.pCenter,a.center)<b.posThreshold;this.pTime=a.timeStamp,this.pCenter=a.center,h&&g?this.count+=1:this.count=1,this._input=a;var i=this.count%b.taps;if(0===i)return this.hasRequireFailures()?(this._timer=e(function(){this.state=rb,this.tryEmit()},b.interval,this),ob):rb}return tb},failTimeout:function(){return this._timer=e(function(){this.state=tb},this.options.interval,this),tb},reset:function(){clearTimeout(this._timer)},emit:function(){this.state==rb&&(this._input.tapCount=this.count,this.manager.emit(this.options.event,this._input))}}),ha.VERSION="2.0.8",ha.defaults={domEvents:!1,touchAction:gb,enable:!0,inputTarget:null,inputClass:null,preset:[[ea,{enable:!1}],[ca,{enable:!1},["rotate"]],[fa,{direction:Na}],[ba,{direction:Na},["swipe"]],[ga],[ga,{event:"doubletap",taps:2},["tap"]],[da]],cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}};var ub=1,vb=2;ia.prototype={set:function(a){return la(this.options,a),a.touchAction&&this.touchAction.update(),a.inputTarget&&(this.input.destroy(),this.input.target=a.inputTarget,this.input.init()),this},stop:function(a){this.session.stopped=a?vb:ub},recognize:function(a){var b=this.session;if(!b.stopped){this.touchAction.preventDefaults(a);var c,d=this.recognizers,e=b.curRecognizer;(!e||e&&e.state&rb)&&(e=b.curRecognizer=null);for(var f=0;f<d.length;)c=d[f],b.stopped===vb||e&&c!=e&&!c.canRecognizeWith(e)?c.reset():c.recognize(a),!e&&c.state&(ob|pb|qb)&&(e=b.curRecognizer=c),f++}},get:function(a){if(a instanceof Y)return a;for(var b=this.recognizers,c=0;c<b.length;c++)if(b[c].options.event==a)return b[c];return null},add:function(a){if(f(a,"add",this))return this;var b=this.get(a.options.event);return b&&this.remove(b),this.recognizers.push(a),a.manager=this,this.touchAction.update(),a},remove:function(a){if(f(a,"remove",this))return this;if(a=this.get(a)){var b=this.recognizers,c=r(b,a);-1!==c&&(b.splice(c,1),this.touchAction.update())}return this},on:function(a,b){if(a!==d&&b!==d){var c=this.handlers;return g(q(a),function(a){c[a]=c[a]||[],c[a].push(b)}),this}},off:function(a,b){if(a!==d){var c=this.handlers;return g(q(a),function(a){b?c[a]&&c[a].splice(r(c[a],b),1):delete c[a]}),this}},emit:function(a,b){this.options.domEvents&&ka(a,b);var c=this.handlers[a]&&this.handlers[a].slice();if(c&&c.length){b.type=a,b.preventDefault=function(){b.srcEvent.preventDefault()};for(var d=0;d<c.length;)c[d](b),d++}},destroy:function(){this.element&&ja(this,!1),this.handlers={},this.session={},this.input.destroy(),this.element=null}},la(ha,{INPUT_START:Ea,INPUT_MOVE:Fa,INPUT_END:Ga,INPUT_CANCEL:Ha,STATE_POSSIBLE:nb,STATE_BEGAN:ob,STATE_CHANGED:pb,STATE_ENDED:qb,STATE_RECOGNIZED:rb,STATE_CANCELLED:sb,STATE_FAILED:tb,DIRECTION_NONE:Ia,DIRECTION_LEFT:Ja,DIRECTION_RIGHT:Ka,DIRECTION_UP:La,DIRECTION_DOWN:Ma,DIRECTION_HORIZONTAL:Na,DIRECTION_VERTICAL:Oa,DIRECTION_ALL:Pa,Manager:ia,Input:x,TouchAction:V,TouchInput:P,MouseInput:L,PointerEventInput:M,TouchMouseInput:R,SingleTouchInput:N,Recognizer:Y,AttrRecognizer:aa,Tap:ga,Pan:ba,Swipe:fa,Pinch:ca,Rotate:ea,Press:da,on:m,off:n,each:g,merge:ta,extend:sa,assign:la,inherit:i,bindFn:j,prefixed:u});var wb="undefined"!=typeof a?a:"undefined"!=typeof self?self:{};wb.Hammer=ha,"function"==typeof define&&define.amd?define(function(){return ha}):"undefined"!=typeof module&&module.exports?module.exports=ha:a[c]=ha}(window,document,"Hammer");
\ No newline at end of file
diff --git a/src/main/resources/static/obj-viewer/hammer.modified.js b/src/main/resources/static/obj-viewer/hammer.modified.js
deleted file mode 100644 (file)
index fcbf3bc..0000000
+++ /dev/null
@@ -1,2643 +0,0 @@
-/*! Hammer.JS - v2.0.8 - 2016-04-23
- * http://hammerjs.github.io/
- *
- * Copyright (c) 2016 Jorik Tangelder;
- * Licensed under the MIT license */
-(function(window, document, exportName, undefined) {
-       'use strict';
-
-       var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
-       var TEST_ELEMENT = document.createElement('div');
-
-       var TYPE_FUNCTION = 'function';
-
-       var round = Math.round;
-       var abs = Math.abs;
-       var now = Date.now;
-
-       /**
-        * set a timeout with a given scope
-        * @param {Function} fn
-        * @param {Number} timeout
-        * @param {Object} context
-        * @returns {number}
-        */
-       function setTimeoutContext(fn, timeout, context) {
-               return setTimeout(bindFn(fn, context), timeout);
-       }
-
-       /**
-        * if the argument is an array, we want to execute the fn on each entry
-        * if it aint an array we don't want to do a thing.
-        * this is used by all the methods that accept a single and array argument.
-        * @param {*|Array} arg
-        * @param {String} fn
-        * @param {Object} [context]
-        * @returns {Boolean}
-        */
-       function invokeArrayArg(arg, fn, context) {
-               if (Array.isArray(arg)) {
-                       each(arg, context[fn], context);
-                       return true;
-               }
-               return false;
-       }
-
-       /**
-        * walk objects and arrays
-        * @param {Object} obj
-        * @param {Function} iterator
-        * @param {Object} context
-        */
-       function each(obj, iterator, context) {
-               var i;
-
-               if (!obj) {
-                       return;
-               }
-
-               if (obj.forEach) {
-                       obj.forEach(iterator, context);
-               } else if (obj.length !== undefined) {
-                       i = 0;
-                       while (i < obj.length) {
-                               iterator.call(context, obj[i], i, obj);
-                               i++;
-                       }
-               } else {
-                       for (i in obj) {
-                               obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
-                       }
-               }
-       }
-
-       /**
-        * wrap a method with a deprecation warning and stack trace
-        * @param {Function} method
-        * @param {String} name
-        * @param {String} message
-        * @returns {Function} A new function wrapping the supplied method.
-        */
-       function deprecate(method, name, message) {
-               var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
-               return function() {
-                       var e = new Error('get-stack-trace');
-                       var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
-                               .replace(/^\s+at\s+/gm, '')
-                               .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
-
-                       var log = window.console && (window.console.warn || window.console.log);
-                       if (log) {
-                               log.call(window.console, deprecationMessage, stack);
-                       }
-                       return method.apply(this, arguments);
-               };
-       }
-
-       /**
-        * extend object.
-        * means that properties in dest will be overwritten by the ones in src.
-        * @param {Object} target
-        * @param {...Object} objects_to_assign
-        * @returns {Object} target
-        */
-       var assign;
-       if (typeof Object.assign !== 'function') {
-               assign = function assign(target) {
-                       if (target === undefined || target === null) {
-                               throw new TypeError('Cannot convert undefined or null to object');
-                       }
-
-                       var output = Object(target);
-                       for (var index = 1; index < arguments.length; index++) {
-                               var source = arguments[index];
-                               if (source !== undefined && source !== null) {
-                                       for (var nextKey in source) {
-                                               if (source.hasOwnProperty(nextKey)) {
-                                                       output[nextKey] = source[nextKey];
-                                               }
-                                       }
-                               }
-                       }
-                       return output;
-               };
-       } else {
-               assign = Object.assign;
-       }
-
-       /**
-        * extend object.
-        * means that properties in dest will be overwritten by the ones in src.
-        * @param {Object} dest
-        * @param {Object} src
-        * @param {Boolean} [merge=false]
-        * @returns {Object} dest
-        */
-       var extend = deprecate(function extend(dest, src, merge) {
-               var keys = Object.keys(src);
-               var i = 0;
-               while (i < keys.length) {
-                       if (!merge || (merge && dest[keys[i]] === undefined)) {
-                               dest[keys[i]] = src[keys[i]];
-                       }
-                       i++;
-               }
-               return dest;
-       }, 'extend', 'Use `assign`.');
-
-       /**
-        * merge the values from src in the dest.
-        * means that properties that exist in dest will not be overwritten by src
-        * @param {Object} dest
-        * @param {Object} src
-        * @returns {Object} dest
-        */
-       var merge = deprecate(function merge(dest, src) {
-               return extend(dest, src, true);
-       }, 'merge', 'Use `assign`.');
-
-       /**
-        * simple class inheritance
-        * @param {Function} child
-        * @param {Function} base
-        * @param {Object} [properties]
-        */
-       function inherit(child, base, properties) {
-               var baseP = base.prototype,
-                       childP;
-
-               childP = child.prototype = Object.create(baseP);
-               childP.constructor = child;
-               childP._super = baseP;
-
-               if (properties) {
-                       assign(childP, properties);
-               }
-       }
-
-       /**
-        * simple function bind
-        * @param {Function} fn
-        * @param {Object} context
-        * @returns {Function}
-        */
-       function bindFn(fn, context) {
-               return function boundFn() {
-                       return fn.apply(context, arguments);
-               };
-       }
-
-       /**
-        * let a boolean value also be a function that must return a boolean
-        * this first item in args will be used as the context
-        * @param {Boolean|Function} val
-        * @param {Array} [args]
-        * @returns {Boolean}
-        */
-       function boolOrFn(val, args) {
-               if (typeof val == TYPE_FUNCTION) {
-                       return val.apply(args ? args[0] || undefined : undefined, args);
-               }
-               return val;
-       }
-
-       /**
-        * use the val2 when val1 is undefined
-        * @param {*} val1
-        * @param {*} val2
-        * @returns {*}
-        */
-       function ifUndefined(val1, val2) {
-               return (val1 === undefined) ? val2 : val1;
-       }
-
-       /**
-        * addEventListener with multiple events at once
-        * @param {EventTarget} target
-        * @param {String} types
-        * @param {Function} handler
-        */
-       function addEventListeners(target, types, handler) {
-               each(splitStr(types), function(type) {
-                       target.addEventListener(type, handler, false);
-               });
-       }
-
-       /**
-        * removeEventListener with multiple events at once
-        * @param {EventTarget} target
-        * @param {String} types
-        * @param {Function} handler
-        */
-       function removeEventListeners(target, types, handler) {
-               each(splitStr(types), function(type) {
-                       target.removeEventListener(type, handler, false);
-               });
-       }
-
-       /**
-        * find if a node is in the given parent
-        * @method hasParent
-        * @param {HTMLElement} node
-        * @param {HTMLElement} parent
-        * @return {Boolean} found
-        */
-       function hasParent(node, parent) {
-               while (node) {
-                       if (node == parent) {
-                               return true;
-                       }
-                       node = node.parentNode;
-               }
-               return false;
-       }
-
-       /**
-        * small indexOf wrapper
-        * @param {String} str
-        * @param {String} find
-        * @returns {Boolean} found
-        */
-       function inStr(str, find) {
-               return str.indexOf(find) > -1;
-       }
-
-       /**
-        * split string on whitespace
-        * @param {String} str
-        * @returns {Array} words
-        */
-       function splitStr(str) {
-               return str.trim().split(/\s+/g);
-       }
-
-       /**
-        * find if a array contains the object using indexOf or a simple polyFill
-        * @param {Array} src
-        * @param {String} find
-        * @param {String} [findByKey]
-        * @return {Boolean|Number} false when not found, or the index
-        */
-       function inArray(src, find, findByKey) {
-               if (src.indexOf && !findByKey) {
-                       return src.indexOf(find);
-               } else {
-                       var i = 0;
-                       while (i < src.length) {
-                               if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
-                                       return i;
-                               }
-                               i++;
-                       }
-                       return -1;
-               }
-       }
-
-       /**
-        * convert array-like objects to real arrays
-        * @param {Object} obj
-        * @returns {Array}
-        */
-       function toArray(obj) {
-               return Array.prototype.slice.call(obj, 0);
-       }
-
-       /**
-        * unique array with objects based on a key (like 'id') or just by the array's value
-        * @param {Array} src [{id:1},{id:2},{id:1}]
-        * @param {String} [key]
-        * @param {Boolean} [sort=False]
-        * @returns {Array} [{id:1},{id:2}]
-        */
-       function uniqueArray(src, key, sort) {
-               var results = [];
-               var values = [];
-               var i = 0;
-
-               while (i < src.length) {
-                       var val = key ? src[i][key] : src[i];
-                       if (inArray(values, val) < 0) {
-                               results.push(src[i]);
-                       }
-                       values[i] = val;
-                       i++;
-               }
-
-               if (sort) {
-                       if (!key) {
-                               results = results.sort();
-                       } else {
-                               results = results.sort(function sortUniqueArray(a, b) {
-                                       return a[key] > b[key];
-                               });
-                       }
-               }
-
-               return results;
-       }
-
-       /**
-        * get the prefixed property
-        * @param {Object} obj
-        * @param {String} property
-        * @returns {String|Undefined} prefixed
-        */
-       function prefixed(obj, property) {
-               var prefix, prop;
-               var camelProp = property[0].toUpperCase() + property.slice(1);
-
-               var i = 0;
-               while (i < VENDOR_PREFIXES.length) {
-                       prefix = VENDOR_PREFIXES[i];
-                       prop = (prefix) ? prefix + camelProp : property;
-
-                       if (prop in obj) {
-                               return prop;
-                       }
-                       i++;
-               }
-               return undefined;
-       }
-
-       /**
-        * get a unique id
-        * @returns {number} uniqueId
-        */
-       var _uniqueId = 1;
-       function uniqueId() {
-               return _uniqueId++;
-       }
-
-       /**
-        * get the window object of an element
-        * @param {HTMLElement} element
-        * @returns {DocumentView|Window}
-        */
-       function getWindowForElement(element) {
-               var doc = element.ownerDocument || element;
-               return (doc.defaultView || doc.parentWindow || window);
-       }
-
-       var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
-
-       var SUPPORT_TOUCH = ('ontouchstart' in window);
-       var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
-       var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);
-
-       var INPUT_TYPE_TOUCH = 'touch';
-       var INPUT_TYPE_PEN = 'pen';
-       var INPUT_TYPE_MOUSE = 'mouse';
-       var INPUT_TYPE_KINECT = 'kinect';
-
-       var COMPUTE_INTERVAL = 25;
-
-       var INPUT_START = 1;
-       var INPUT_MOVE = 2;
-       var INPUT_END = 4;
-       var INPUT_CANCEL = 8;
-
-       var DIRECTION_NONE = 1;
-       var DIRECTION_LEFT = 2;
-       var DIRECTION_RIGHT = 4;
-       var DIRECTION_UP = 8;
-       var DIRECTION_DOWN = 16;
-
-       var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
-       var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
-       var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
-
-       var PROPS_XY = ['x', 'y'];
-       var PROPS_CLIENT_XY = ['screenX', 'screenY'];
-
-       /**
-        * create new input type manager
-        * @param {Manager} manager
-        * @param {Function} callback
-        * @returns {Input}
-        * @constructor
-        */
-       function Input(manager, callback) {
-               var self = this;
-               this.manager = manager;
-               this.callback = callback;
-               this.element = manager.element;
-               this.target = manager.options.inputTarget;
-
-               // smaller wrapper around the handler, for the scope and the enabled state of the manager,
-               // so when disabled the input events are completely bypassed.
-               this.domHandler = function(ev) {
-                       if (boolOrFn(manager.options.enable, [manager])) {
-                               self.handler(ev);
-                       }
-               };
-
-               this.init();
-
-       }
-
-       Input.prototype = {
-               /**
-                * should handle the inputEvent data and trigger the callback
-                * @virtual
-                */
-               handler: function() { },
-
-               /**
-                * bind the events
-                */
-               init: function() {
-                       this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
-                       this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
-                       this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
-               },
-
-               /**
-                * unbind the events
-                */
-               destroy: function() {
-                       this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
-                       this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
-                       this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
-               }
-       };
-
-       /**
-        * create new input type manager
-        * called by the Manager constructor
-        * @param {Hammer} manager
-        * @returns {Input}
-        */
-       function createInputInstance(manager) {
-               var Type;
-               var inputClass = manager.options.inputClass;
-
-               if (inputClass) {
-                       Type = inputClass;
-               } else if (SUPPORT_POINTER_EVENTS) {
-                       Type = PointerEventInput;
-               } else if (SUPPORT_ONLY_TOUCH) {
-                       Type = TouchInput;
-               } else if (!SUPPORT_TOUCH) {
-                       Type = MouseInput;
-               } else {
-                       Type = TouchMouseInput;
-               }
-               return new (Type)(manager, inputHandler);
-       }
-
-       /**
-        * handle input events
-        * @param {Manager} manager
-        * @param {String} eventType
-        * @param {Object} input
-        */
-       function inputHandler(manager, eventType, input) {
-               var pointersLen = input.pointers.length;
-               var changedPointersLen = input.changedPointers.length;
-               var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
-               var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));
-
-               input.isFirst = !!isFirst;
-               input.isFinal = !!isFinal;
-
-               if (isFirst) {
-                       manager.session = {};
-               }
-
-               // source event is the normalized value of the domEvents
-               // like 'touchstart, mouseup, pointerdown'
-               input.eventType = eventType;
-
-               // compute scale, rotation etc
-               computeInputData(manager, input);
-
-               // emit secret event
-               manager.emit('hammer.input', input);
-
-               manager.recognize(input);
-               manager.session.prevInput = input;
-       }
-
-       /**
-        * extend the data with some usable properties like scale, rotate, velocity etc
-        * @param {Object} manager
-        * @param {Object} input
-        */
-       function computeInputData(manager, input) {
-               var session = manager.session;
-               var pointers = input.pointers;
-               var pointersLength = pointers.length;
-
-               // store the first input to calculate the distance and direction
-               if (!session.firstInput) {
-                       session.firstInput = simpleCloneInputData(input);
-               }
-
-               // to compute scale and rotation we need to store the multiple touches
-               if (pointersLength > 1 && !session.firstMultiple) {
-                       session.firstMultiple = simpleCloneInputData(input);
-               } else if (pointersLength === 1) {
-                       session.firstMultiple = false;
-               }
-
-               var firstInput = session.firstInput;
-               var firstMultiple = session.firstMultiple;
-               var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;
-
-               var center = input.center = getCenter(pointers);
-               input.timeStamp = now();
-               input.deltaTime = input.timeStamp - firstInput.timeStamp;
-
-               input.angle = getAngle(offsetCenter, center);
-               input.distance = getDistance(offsetCenter, center);
-
-               computeDeltaXY(session, input);
-               input.offsetDirection = getDirection(input.deltaX, input.deltaY);
-
-               var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
-               input.overallVelocityX = overallVelocity.x;
-               input.overallVelocityY = overallVelocity.y;
-               input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
-
-               input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
-               input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
-
-               input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
-                       session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
-
-               computeIntervalInputData(session, input);
-
-               // find the correct target
-               var target = manager.element;
-               if (hasParent(input.srcEvent.target, target)) {
-                       target = input.srcEvent.target;
-               }
-               input.target = target;
-       }
-
-       function computeDeltaXY(session, input) {
-               var center = input.center;
-               var offset = session.offsetDelta || {};
-               var prevDelta = session.prevDelta || {};
-               var prevInput = session.prevInput || {};
-
-               if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
-                       prevDelta = session.prevDelta = {
-                               x: prevInput.deltaX || 0,
-                               y: prevInput.deltaY || 0
-                       };
-
-                       offset = session.offsetDelta = {
-                               x: center.x,
-                               y: center.y
-                       };
-               }
-
-               input.deltaX = prevDelta.x + (center.x - offset.x);
-               input.deltaY = prevDelta.y + (center.y - offset.y);
-       }
-
-       /**
-        * velocity is calculated every x ms
-        * @param {Object} session
-        * @param {Object} input
-        */
-       function computeIntervalInputData(session, input) {
-               var last = session.lastInterval || input,
-                       deltaTime = input.timeStamp - last.timeStamp,
-                       velocity, velocityX, velocityY, direction;
-
-               if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
-                       var deltaX = input.deltaX - last.deltaX;
-                       var deltaY = input.deltaY - last.deltaY;
-
-                       var v = getVelocity(deltaTime, deltaX, deltaY);
-                       velocityX = v.x;
-                       velocityY = v.y;
-                       velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
-                       direction = getDirection(deltaX, deltaY);
-
-                       session.lastInterval = input;
-               } else {
-                       // use latest velocity info if it doesn't overtake a minimum period
-                       velocity = last.velocity;
-                       velocityX = last.velocityX;
-                       velocityY = last.velocityY;
-                       direction = last.direction;
-               }
-
-               input.velocity = velocity;
-               input.velocityX = velocityX;
-               input.velocityY = velocityY;
-               input.direction = direction;
-       }
-
-       /**
-        * create a simple clone from the input used for storage of firstInput and firstMultiple
-        * @param {Object} input
-        * @returns {Object} clonedInputData
-        */
-       function simpleCloneInputData(input) {
-               // make a simple copy of the pointers because we will get a reference if we don't
-               // we only need screenXY for the calculations
-               var pointers = [];
-               var i = 0;
-               while (i < input.pointers.length) {
-                       pointers[i] = {
-                               screenX: round(input.pointers[i].screenX),
-                               screenY: round(input.pointers[i].screenY)
-                       };
-                       i++;
-               }
-
-               return {
-                       timeStamp: now(),
-                       pointers: pointers,
-                       center: getCenter(pointers),
-                       deltaX: input.deltaX,
-                       deltaY: input.deltaY
-               };
-       }
-
-       /**
-        * get the center of all the pointers
-        * @param {Array} pointers
-        * @return {Object} center contains `x` and `y` properties
-        */
-       function getCenter(pointers) {
-               var pointersLength = pointers.length;
-
-               // no need to loop when only one touch
-               if (pointersLength === 1) {
-                       return {
-                               x: round(pointers[0].screenX),
-                               y: round(pointers[0].screenY)
-                       };
-               }
-
-               var x = 0, y = 0, i = 0;
-               while (i < pointersLength) {
-                       x += pointers[i].screenX;
-                       y += pointers[i].screenY;
-                       i++;
-               }
-
-               return {
-                       x: round(x / pointersLength),
-                       y: round(y / pointersLength)
-               };
-       }
-
-       /**
-        * calculate the velocity between two points. unit is in px per ms.
-        * @param {Number} deltaTime
-        * @param {Number} x
-        * @param {Number} y
-        * @return {Object} velocity `x` and `y`
-        */
-       function getVelocity(deltaTime, x, y) {
-               return {
-                       x: x / deltaTime || 0,
-                       y: y / deltaTime || 0
-               };
-       }
-
-       /**
-        * get the direction between two points
-        * @param {Number} x
-        * @param {Number} y
-        * @return {Number} direction
-        */
-       function getDirection(x, y) {
-               if (x === y) {
-                       return DIRECTION_NONE;
-               }
-
-               if (abs(x) >= abs(y)) {
-                       return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
-               }
-               return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
-       }
-
-       /**
-        * calculate the absolute distance between two points
-        * @param {Object} p1 {x, y}
-        * @param {Object} p2 {x, y}
-        * @param {Array} [props] containing x and y keys
-        * @return {Number} distance
-        */
-       function getDistance(p1, p2, props) {
-               if (!props) {
-                       props = PROPS_XY;
-               }
-               var x = p2[props[0]] - p1[props[0]],
-                       y = p2[props[1]] - p1[props[1]];
-
-               return Math.sqrt((x * x) + (y * y));
-       }
-
-       /**
-        * calculate the angle between two coordinates
-        * @param {Object} p1
-        * @param {Object} p2
-        * @param {Array} [props] containing x and y keys
-        * @return {Number} angle
-        */
-       function getAngle(p1, p2, props) {
-               if (!props) {
-                       props = PROPS_XY;
-               }
-               var x = p2[props[0]] - p1[props[0]],
-                       y = p2[props[1]] - p1[props[1]];
-               return Math.atan2(y, x) * 180 / Math.PI;
-       }
-
-       /**
-        * calculate the rotation degrees between two pointersets
-        * @param {Array} start array of pointers
-        * @param {Array} end array of pointers
-        * @return {Number} rotation
-        */
-       function getRotation(start, end) {
-               return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
-       }
-
-       /**
-        * calculate the scale factor between two pointersets
-        * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
-        * @param {Array} start array of pointers
-        * @param {Array} end array of pointers
-        * @return {Number} scale
-        */
-       function getScale(start, end) {
-               return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
-       }
-
-       var MOUSE_INPUT_MAP = {
-               mousedown: INPUT_START,
-               mousemove: INPUT_MOVE,
-               mouseup: INPUT_END
-       };
-
-       var MOUSE_ELEMENT_EVENTS = 'mousedown';
-       var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';
-
-       /**
-        * Mouse events input
-        * @constructor
-        * @extends Input
-        */
-       function MouseInput() {
-               this.evEl = MOUSE_ELEMENT_EVENTS;
-               this.evWin = MOUSE_WINDOW_EVENTS;
-
-               this.pressed = false; // mousedown state
-
-               Input.apply(this, arguments);
-       }
-
-       inherit(MouseInput, Input, {
-               /**
-                * handle mouse events
-                * @param {Object} ev
-                */
-               handler: function MEhandler(ev) {
-                       var eventType = MOUSE_INPUT_MAP[ev.type];
-
-                       // on start we want to have the left mouse button down
-                       if (eventType & INPUT_START && ev.button === 0) {
-                               this.pressed = true;
-                       }
-
-                       if (eventType & INPUT_MOVE && ev.which !== 1) {
-                               eventType = INPUT_END;
-                       }
-
-                       // mouse must be down
-                       if (!this.pressed) {
-                               return;
-                       }
-
-                       if (eventType & INPUT_END) {
-                               this.pressed = false;
-                       }
-
-                       this.callback(this.manager, eventType, {
-                               pointers: [ev],
-                               changedPointers: [ev],
-                               pointerType: INPUT_TYPE_MOUSE,
-                               srcEvent: ev
-                       });
-               }
-       });
-
-       var POINTER_INPUT_MAP = {
-               pointerdown: INPUT_START,
-               pointermove: INPUT_MOVE,
-               pointerup: INPUT_END,
-               pointercancel: INPUT_CANCEL,
-               pointerout: INPUT_CANCEL
-       };
-
-// in IE10 the pointer types is defined as an enum
-       var IE10_POINTER_TYPE_ENUM = {
-               2: INPUT_TYPE_TOUCH,
-               3: INPUT_TYPE_PEN,
-               4: INPUT_TYPE_MOUSE,
-               5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
-       };
-
-       var POINTER_ELEMENT_EVENTS = 'pointerdown';
-       var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
-
-// IE10 has prefixed support, and case-sensitive
-       if (window.MSPointerEvent && !window.PointerEvent) {
-               POINTER_ELEMENT_EVENTS = 'MSPointerDown';
-               POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
-       }
-
-       /**
-        * Pointer events input
-        * @constructor
-        * @extends Input
-        */
-       function PointerEventInput() {
-               this.evEl = POINTER_ELEMENT_EVENTS;
-               this.evWin = POINTER_WINDOW_EVENTS;
-
-               Input.apply(this, arguments);
-
-               this.store = (this.manager.session.pointerEvents = []);
-       }
-
-       inherit(PointerEventInput, Input, {
-               /**
-                * handle mouse events
-                * @param {Object} ev
-                */
-               handler: function PEhandler(ev) {
-                       var store = this.store;
-                       var removePointer = false;
-
-                       var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
-                       var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
-                       var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
-
-                       var isTouch = (pointerType == INPUT_TYPE_TOUCH);
-
-                       // get index of the event in the store
-                       var storeIndex = inArray(store, ev.pointerId, 'pointerId');
-
-                       // start and mouse must be down
-                       if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
-                               if (storeIndex < 0) {
-                                       store.push(ev);
-                                       storeIndex = store.length - 1;
-                               }
-                       } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
-                               removePointer = true;
-                       }
-
-                       // it not found, so the pointer hasn't been down (so it's probably a hover)
-                       if (storeIndex < 0) {
-                               return;
-                       }
-
-                       // update the event in the store
-                       store[storeIndex] = ev;
-
-                       this.callback(this.manager, eventType, {
-                               pointers: store,
-                               changedPointers: [ev],
-                               pointerType: pointerType,
-                               srcEvent: ev
-                       });
-
-                       if (removePointer) {
-                               // remove from the store
-                               store.splice(storeIndex, 1);
-                       }
-               }
-       });
-
-       var SINGLE_TOUCH_INPUT_MAP = {
-               touchstart: INPUT_START,
-               touchmove: INPUT_MOVE,
-               touchend: INPUT_END,
-               touchcancel: INPUT_CANCEL
-       };
-
-       var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
-       var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';
-
-       /**
-        * Touch events input
-        * @constructor
-        * @extends Input
-        */
-       function SingleTouchInput() {
-               this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
-               this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
-               this.started = false;
-
-               Input.apply(this, arguments);
-       }
-
-       inherit(SingleTouchInput, Input, {
-               handler: function TEhandler(ev) {
-                       var type = SINGLE_TOUCH_INPUT_MAP[ev.type];
-
-                       // should we handle the touch events?
-                       if (type === INPUT_START) {
-                               this.started = true;
-                       }
-
-                       if (!this.started) {
-                               return;
-                       }
-
-                       var touches = normalizeSingleTouches.call(this, ev, type);
-
-                       // when done, reset the started state
-                       if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
-                               this.started = false;
-                       }
-
-                       this.callback(this.manager, type, {
-                               pointers: touches[0],
-                               changedPointers: touches[1],
-                               pointerType: INPUT_TYPE_TOUCH,
-                               srcEvent: ev
-                       });
-               }
-       });
-
-       /**
-        * @this {TouchInput}
-        * @param {Object} ev
-        * @param {Number} type flag
-        * @returns {undefined|Array} [all, changed]
-        */
-       function normalizeSingleTouches(ev, type) {
-               var all = toArray(ev.touches);
-               var changed = toArray(ev.changedTouches);
-
-               if (type & (INPUT_END | INPUT_CANCEL)) {
-                       all = uniqueArray(all.concat(changed), 'identifier', true);
-               }
-
-               return [all, changed];
-       }
-
-       var TOUCH_INPUT_MAP = {
-               touchstart: INPUT_START,
-               touchmove: INPUT_MOVE,
-               touchend: INPUT_END,
-               touchcancel: INPUT_CANCEL
-       };
-
-       var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';
-
-       /**
-        * Multi-user touch events input
-        * @constructor
-        * @extends Input
-        */
-       function TouchInput() {
-               this.evTarget = TOUCH_TARGET_EVENTS;
-               this.targetIds = {};
-
-               Input.apply(this, arguments);
-       }
-
-       inherit(TouchInput, Input, {
-               handler: function MTEhandler(ev) {
-                       var type = TOUCH_INPUT_MAP[ev.type];
-                       var touches = getTouches.call(this, ev, type);
-                       if (!touches) {
-                               return;
-                       }
-
-                       this.callback(this.manager, type, {
-                               pointers: touches[0],
-                               changedPointers: touches[1],
-                               pointerType: INPUT_TYPE_TOUCH,
-                               srcEvent: ev
-                       });
-               }
-       });
-
-       /**
-        * @this {TouchInput}
-        * @param {Object} ev
-        * @param {Number} type flag
-        * @returns {undefined|Array} [all, changed]
-        */
-       function getTouches(ev, type) {
-               var allTouches = toArray(ev.touches);
-               var targetIds = this.targetIds;
-
-               // when there is only one touch, the process can be simplified
-               if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
-                       targetIds[allTouches[0].identifier] = true;
-                       return [allTouches, allTouches];
-               }
-
-               var i,
-                       targetTouches,
-                       changedTouches = toArray(ev.changedTouches),
-                       changedTargetTouches = [],
-                       target = this.target;
-
-               // get target touches from touches
-               targetTouches = allTouches.filter(function(touch) {
-                       return hasParent(touch.target, target);
-               });
-
-               // collect touches
-               if (type === INPUT_START) {
-                       i = 0;
-                       while (i < targetTouches.length) {
-                               targetIds[targetTouches[i].identifier] = true;
-                               i++;
-                       }
-               }
-
-               // filter changed touches to only contain touches that exist in the collected target ids
-               i = 0;
-               while (i < changedTouches.length) {
-                       if (targetIds[changedTouches[i].identifier]) {
-                               changedTargetTouches.push(changedTouches[i]);
-                       }
-
-                       // cleanup removed touches
-                       if (type & (INPUT_END | INPUT_CANCEL)) {
-                               delete targetIds[changedTouches[i].identifier];
-                       }
-                       i++;
-               }
-
-               if (!changedTargetTouches.length) {
-                       return;
-               }
-
-               return [
-                       // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
-                       uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
-                       changedTargetTouches
-               ];
-       }
-
-       /**
-        * Combined touch and mouse input
-        *
-        * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
-        * This because touch devices also emit mouse events while doing a touch.
-        *
-        * @constructor
-        * @extends Input
-        */
-
-       var DEDUP_TIMEOUT = 2500;
-       var DEDUP_DISTANCE = 25;
-
-       function TouchMouseInput() {
-               Input.apply(this, arguments);
-
-               var handler = bindFn(this.handler, this);
-               this.touch = new TouchInput(this.manager, handler);
-               this.mouse = new MouseInput(this.manager, handler);
-
-               this.primaryTouch = null;
-               this.lastTouches = [];
-       }
-
-       inherit(TouchMouseInput, Input, {
-               /**
-                * handle mouse and touch events
-                * @param {Hammer} manager
-                * @param {String} inputEvent
-                * @param {Object} inputData
-                */
-               handler: function TMEhandler(manager, inputEvent, inputData) {
-                       var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
-                               isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);
-
-                       if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
-                               return;
-                       }
-
-                       // when we're in a touch event, record touches to  de-dupe synthetic mouse event
-                       if (isTouch) {
-                               recordTouches.call(this, inputEvent, inputData);
-                       } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
-                               return;
-                       }
-
-                       this.callback(manager, inputEvent, inputData);
-               },
-
-               /**
-                * remove the event listeners
-                */
-               destroy: function destroy() {
-                       this.touch.destroy();
-                       this.mouse.destroy();
-               }
-       });
-
-       function recordTouches(eventType, eventData) {
-               if (eventType & INPUT_START) {
-                       this.primaryTouch = eventData.changedPointers[0].identifier;
-                       setLastTouch.call(this, eventData);
-               } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
-                       setLastTouch.call(this, eventData);
-               }
-       }
-
-       function setLastTouch(eventData) {
-               var touch = eventData.changedPointers[0];
-
-               if (touch.identifier === this.primaryTouch) {
-                       var lastTouch = {x: touch.screenX, y: touch.screenY};
-                       this.lastTouches.push(lastTouch);
-                       var lts = this.lastTouches;
-                       var removeLastTouch = function() {
-                               var i = lts.indexOf(lastTouch);
-                               if (i > -1) {
-                                       lts.splice(i, 1);
-                               }
-                       };
-                       setTimeout(removeLastTouch, DEDUP_TIMEOUT);
-               }
-       }
-
-       function isSyntheticEvent(eventData) {
-               var x = eventData.srcEvent.screenX, y = eventData.srcEvent.screenY;
-               for (var i = 0; i < this.lastTouches.length; i++) {
-                       var t = this.lastTouches[i];
-                       var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
-                       if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
-                               return true;
-                       }
-               }
-               return false;
-       }
-
-       var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
-       var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;
-
-// magical touchAction value
-       var TOUCH_ACTION_COMPUTE = 'compute';
-       var TOUCH_ACTION_AUTO = 'auto';
-       var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
-       var TOUCH_ACTION_NONE = 'none';
-       var TOUCH_ACTION_PAN_X = 'pan-x';
-       var TOUCH_ACTION_PAN_Y = 'pan-y';
-       var TOUCH_ACTION_MAP = getTouchActionProps();
-
-       /**
-        * Touch Action
-        * sets the touchAction property or uses the js alternative
-        * @param {Manager} manager
-        * @param {String} value
-        * @constructor
-        */
-       function TouchAction(manager, value) {
-               this.manager = manager;
-               this.set(value);
-       }
-
-       TouchAction.prototype = {
-               /**
-                * set the touchAction value on the element or enable the polyfill
-                * @param {String} value
-                */
-               set: function(value) {
-                       // find out the touch-action by the event handlers
-                       if (value == TOUCH_ACTION_COMPUTE) {
-                               value = this.compute();
-                       }
-
-                       if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
-                               this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
-                       }
-                       this.actions = value.toLowerCase().trim();
-               },
-
-               /**
-                * just re-set the touchAction value
-                */
-               update: function() {
-                       this.set(this.manager.options.touchAction);
-               },
-
-               /**
-                * compute the value for the touchAction property based on the recognizer's settings
-                * @returns {String} value
-                */
-               compute: function() {
-                       var actions = [];
-                       each(this.manager.recognizers, function(recognizer) {
-                               if (boolOrFn(recognizer.options.enable, [recognizer])) {
-                                       actions = actions.concat(recognizer.getTouchAction());
-                               }
-                       });
-                       return cleanTouchActions(actions.join(' '));
-               },
-
-               /**
-                * this method is called on each input cycle and provides the preventing of the browser behavior
-                * @param {Object} input
-                */
-               preventDefaults: function(input) {
-                       var srcEvent = input.srcEvent;
-                       var direction = input.offsetDirection;
-
-                       // if the touch action did prevented once this session
-                       if (this.manager.session.prevented) {
-                               srcEvent.preventDefault();
-                               return;
-                       }
-
-                       var actions = this.actions;
-                       var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
-                       var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
-                       var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];
-
-                       if (hasNone) {
-                               //do not prevent defaults if this is a tap gesture
-
-                               var isTapPointer = input.pointers.length === 1;
-                               var isTapMovement = input.distance < 2;
-                               var isTapTouchTime = input.deltaTime < 250;
-
-                               if (isTapPointer && isTapMovement && isTapTouchTime) {
-                                       return;
-                               }
-                       }
-
-                       if (hasPanX && hasPanY) {
-                               // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
-                               return;
-                       }
-
-                       if (hasNone ||
-                               (hasPanY && direction & DIRECTION_HORIZONTAL) ||
-                               (hasPanX && direction & DIRECTION_VERTICAL)) {
-                               return this.preventSrc(srcEvent);
-                       }
-               },
-
-               /**
-                * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
-                * @param {Object} srcEvent
-                */
-               preventSrc: function(srcEvent) {
-                       this.manager.session.prevented = true;
-                       srcEvent.preventDefault();
-               }
-       };
-
-       /**
-        * when the touchActions are collected they are not a valid value, so we need to clean things up. *
-        * @param {String} actions
-        * @returns {*}
-        */
-       function cleanTouchActions(actions) {
-               // none
-               if (inStr(actions, TOUCH_ACTION_NONE)) {
-                       return TOUCH_ACTION_NONE;
-               }
-
-               var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
-               var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
-
-               // if both pan-x and pan-y are set (different recognizers
-               // for different directions, e.g. horizontal pan but vertical swipe?)
-               // we need none (as otherwise with pan-x pan-y combined none of these
-               // recognizers will work, since the browser would handle all panning
-               if (hasPanX && hasPanY) {
-                       return TOUCH_ACTION_NONE;
-               }
-
-               // pan-x OR pan-y
-               if (hasPanX || hasPanY) {
-                       return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
-               }
-
-               // manipulation
-               if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
-                       return TOUCH_ACTION_MANIPULATION;
-               }
-
-               return TOUCH_ACTION_AUTO;
-       }
-
-       function getTouchActionProps() {
-               if (!NATIVE_TOUCH_ACTION) {
-                       return false;
-               }
-               var touchMap = {};
-               var cssSupports = window.CSS && window.CSS.supports;
-               ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {
-
-                       // If css.supports is not supported but there is native touch-action assume it supports
-                       // all values. This is the case for IE 10 and 11.
-                       touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
-               });
-               return touchMap;
-       }
-
-       /**
-        * Recognizer flow explained; *
-        * All recognizers have the initial state of POSSIBLE when a input session starts.
-        * The definition of a input session is from the first input until the last input, with all it's movement in it. *
-        * Example session for mouse-input: mousedown -> mousemove -> mouseup
-        *
-        * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
-        * which determines with state it should be.
-        *
-        * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
-        * POSSIBLE to give it another change on the next cycle.
-        *
-        *               Possible
-        *                  |
-        *            +-----+---------------+
-        *            |                     |
-        *      +-----+-----+               |
-        *      |           |               |
-        *   Failed      Cancelled          |
-        *                          +-------+------+
-        *                          |              |
-        *                      Recognized       Began
-        *                                         |
-        *                                      Changed
-        *                                         |
-        *                                  Ended/Recognized
-        */
-       var STATE_POSSIBLE = 1;
-       var STATE_BEGAN = 2;
-       var STATE_CHANGED = 4;
-       var STATE_ENDED = 8;
-       var STATE_RECOGNIZED = STATE_ENDED;
-       var STATE_CANCELLED = 16;
-       var STATE_FAILED = 32;
-
-       /**
-        * Recognizer
-        * Every recognizer needs to extend from this class.
-        * @constructor
-        * @param {Object} options
-        */
-       function Recognizer(options) {
-               this.options = assign({}, this.defaults, options || {});
-
-               this.id = uniqueId();
-
-               this.manager = null;
-
-               // default is enable true
-               this.options.enable = ifUndefined(this.options.enable, true);
-
-               this.state = STATE_POSSIBLE;
-
-               this.simultaneous = {};
-               this.requireFail = [];
-       }
-
-       Recognizer.prototype = {
-               /**
-                * @virtual
-                * @type {Object}
-                */
-               defaults: {},
-
-               /**
-                * set options
-                * @param {Object} options
-                * @return {Recognizer}
-                */
-               set: function(options) {
-                       assign(this.options, options);
-
-                       // also update the touchAction, in case something changed about the directions/enabled state
-                       this.manager && this.manager.touchAction.update();
-                       return this;
-               },
-
-               /**
-                * recognize simultaneous with an other recognizer.
-                * @param {Recognizer} otherRecognizer
-                * @returns {Recognizer} this
-                */
-               recognizeWith: function(otherRecognizer) {
-                       if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
-                               return this;
-                       }
-
-                       var simultaneous = this.simultaneous;
-                       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
-                       if (!simultaneous[otherRecognizer.id]) {
-                               simultaneous[otherRecognizer.id] = otherRecognizer;
-                               otherRecognizer.recognizeWith(this);
-                       }
-                       return this;
-               },
-
-               /**
-                * drop the simultaneous link. it doesnt remove the link on the other recognizer.
-                * @param {Recognizer} otherRecognizer
-                * @returns {Recognizer} this
-                */
-               dropRecognizeWith: function(otherRecognizer) {
-                       if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
-                               return this;
-                       }
-
-                       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
-                       delete this.simultaneous[otherRecognizer.id];
-                       return this;
-               },
-
-               /**
-                * recognizer can only run when an other is failing
-                * @param {Recognizer} otherRecognizer
-                * @returns {Recognizer} this
-                */
-               requireFailure: function(otherRecognizer) {
-                       if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
-                               return this;
-                       }
-
-                       var requireFail = this.requireFail;
-                       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
-                       if (inArray(requireFail, otherRecognizer) === -1) {
-                               requireFail.push(otherRecognizer);
-                               otherRecognizer.requireFailure(this);
-                       }
-                       return this;
-               },
-
-               /**
-                * drop the requireFailure link. it does not remove the link on the other recognizer.
-                * @param {Recognizer} otherRecognizer
-                * @returns {Recognizer} this
-                */
-               dropRequireFailure: function(otherRecognizer) {
-                       if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
-                               return this;
-                       }
-
-                       otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
-                       var index = inArray(this.requireFail, otherRecognizer);
-                       if (index > -1) {
-                               this.requireFail.splice(index, 1);
-                       }
-                       return this;
-               },
-
-               /**
-                * has require failures boolean
-                * @returns {boolean}
-                */
-               hasRequireFailures: function() {
-                       return this.requireFail.length > 0;
-               },
-
-               /**
-                * if the recognizer can recognize simultaneous with an other recognizer
-                * @param {Recognizer} otherRecognizer
-                * @returns {Boolean}
-                */
-               canRecognizeWith: function(otherRecognizer) {
-                       return !!this.simultaneous[otherRecognizer.id];
-               },
-
-               /**
-                * You should use `tryEmit` instead of `emit` directly to check
-                * that all the needed recognizers has failed before emitting.
-                * @param {Object} input
-                */
-               emit: function(input) {
-                       var self = this;
-                       var state = this.state;
-
-                       function emit(event) {
-                               self.manager.emit(event, input);
-                       }
-
-                       // 'panstart' and 'panmove'
-                       if (state < STATE_ENDED) {
-                               emit(self.options.event + stateStr(state));
-                       }
-
-                       emit(self.options.event); // simple 'eventName' events
-
-                       if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
-                               emit(input.additionalEvent);
-                       }
-
-                       // panend and pancancel
-                       if (state >= STATE_ENDED) {
-                               emit(self.options.event + stateStr(state));
-                       }
-               },
-
-               /**
-                * Check that all the require failure recognizers has failed,
-                * if true, it emits a gesture event,
-                * otherwise, setup the state to FAILED.
-                * @param {Object} input
-                */
-               tryEmit: function(input) {
-                       if (this.canEmit()) {
-                               return this.emit(input);
-                       }
-                       // it's failing anyway
-                       this.state = STATE_FAILED;
-               },
-
-               /**
-                * can we emit?
-                * @returns {boolean}
-                */
-               canEmit: function() {
-                       var i = 0;
-                       while (i < this.requireFail.length) {
-                               if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
-                                       return false;
-                               }
-                               i++;
-                       }
-                       return true;
-               },
-
-               /**
-                * update the recognizer
-                * @param {Object} inputData
-                */
-               recognize: function(inputData) {
-                       // make a new copy of the inputData
-                       // so we can change the inputData without messing up the other recognizers
-                       var inputDataClone = assign({}, inputData);
-
-                       // is is enabled and allow recognizing?
-                       if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
-                               this.reset();
-                               this.state = STATE_FAILED;
-                               return;
-                       }
-
-                       // reset when we've reached the end
-                       if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
-                               this.state = STATE_POSSIBLE;
-                       }
-
-                       this.state = this.process(inputDataClone);
-
-                       // the recognizer has recognized a gesture
-                       // so trigger an event
-                       if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
-                               this.tryEmit(inputDataClone);
-                       }
-               },
-
-               /**
-                * return the state of the recognizer
-                * the actual recognizing happens in this method
-                * @virtual
-                * @param {Object} inputData
-                * @returns {Const} STATE
-                */
-               process: function(inputData) { }, // jshint ignore:line
-
-               /**
-                * return the preferred touch-action
-                * @virtual
-                * @returns {Array}
-                */
-               getTouchAction: function() { },
-
-               /**
-                * called when the gesture isn't allowed to recognize
-                * like when another is being recognized or it is disabled
-                * @virtual
-                */
-               reset: function() { }
-       };
-
-       /**
-        * get a usable string, used as event postfix
-        * @param {Const} state
-        * @returns {String} state
-        */
-       function stateStr(state) {
-               if (state & STATE_CANCELLED) {
-                       return 'cancel';
-               } else if (state & STATE_ENDED) {
-                       return 'end';
-               } else if (state & STATE_CHANGED) {
-                       return 'move';
-               } else if (state & STATE_BEGAN) {
-                       return 'start';
-               }
-               return '';
-       }
-
-       /**
-        * direction cons to string
-        * @param {Const} direction
-        * @returns {String}
-        */
-       function directionStr(direction) {
-               if (direction == DIRECTION_DOWN) {
-                       return 'down';
-               } else if (direction == DIRECTION_UP) {
-                       return 'up';
-               } else if (direction == DIRECTION_LEFT) {
-                       return 'left';
-               } else if (direction == DIRECTION_RIGHT) {
-                       return 'right';
-               }
-               return '';
-       }
-
-       /**
-        * get a recognizer by name if it is bound to a manager
-        * @param {Recognizer|String} otherRecognizer
-        * @param {Recognizer} recognizer
-        * @returns {Recognizer}
-        */
-       function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
-               var manager = recognizer.manager;
-               if (manager) {
-                       return manager.get(otherRecognizer);
-               }
-               return otherRecognizer;
-       }
-
-       /**
-        * This recognizer is just used as a base for the simple attribute recognizers.
-        * @constructor
-        * @extends Recognizer
-        */
-       function AttrRecognizer() {
-               Recognizer.apply(this, arguments);
-       }
-
-       inherit(AttrRecognizer, Recognizer, {
-               /**
-                * @namespace
-                * @memberof AttrRecognizer
-                */
-               defaults: {
-                       /**
-                        * @type {Number}
-                        * @default 1
-                        */
-                       pointers: 1
-               },
-
-               /**
-                * Used to check if it the recognizer receives valid input, like input.distance > 10.
-                * @memberof AttrRecognizer
-                * @param {Object} input
-                * @returns {Boolean} recognized
-                */
-               attrTest: function(input) {
-                       var optionPointers = this.options.pointers;
-                       return optionPointers === 0 || input.pointers.length === optionPointers;
-               },
-
-               /**
-                * Process the input and return the state for the recognizer
-                * @memberof AttrRecognizer
-                * @param {Object} input
-                * @returns {*} State
-                */
-               process: function(input) {
-                       var state = this.state;
-                       var eventType = input.eventType;
-
-                       var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
-                       var isValid = this.attrTest(input);
-
-                       // on cancel input and we've recognized before, return STATE_CANCELLED
-                       if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
-                               return state | STATE_CANCELLED;
-                       } else if (isRecognized || isValid) {
-                               if (eventType & INPUT_END) {
-                                       return state | STATE_ENDED;
-                               } else if (!(state & STATE_BEGAN)) {
-                                       return STATE_BEGAN;
-                               }
-                               return state | STATE_CHANGED;
-                       }
-                       return STATE_FAILED;
-               }
-       });
-
-       /**
-        * Pan
-        * Recognized when the pointer is down and moved in the allowed direction.
-        * @constructor
-        * @extends AttrRecognizer
-        */
-       function PanRecognizer() {
-               AttrRecognizer.apply(this, arguments);
-
-               this.pX = null;
-               this.pY = null;
-       }
-
-       inherit(PanRecognizer, AttrRecognizer, {
-               /**
-                * @namespace
-                * @memberof PanRecognizer
-                */
-               defaults: {
-                       event: 'pan',
-                       threshold: 10,
-                       pointers: 1,
-                       direction: DIRECTION_ALL
-               },
-
-               getTouchAction: function() {
-                       var direction = this.options.direction;
-                       var actions = [];
-                       if (direction & DIRECTION_HORIZONTAL) {
-                               actions.push(TOUCH_ACTION_PAN_Y);
-                       }
-                       if (direction & DIRECTION_VERTICAL) {
-                               actions.push(TOUCH_ACTION_PAN_X);
-                       }
-                       return actions;
-               },
-
-               directionTest: function(input) {
-                       var options = this.options;
-                       var hasMoved = true;
-                       var distance = input.distance;
-                       var direction = input.direction;
-                       var x = input.deltaX;
-                       var y = input.deltaY;
-
-                       // lock to axis?
-                       if (!(direction & options.direction)) {
-                               if (options.direction & DIRECTION_HORIZONTAL) {
-                                       direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
-                                       hasMoved = x != this.pX;
-                                       distance = Math.abs(input.deltaX);
-                               } else {
-                                       direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
-                                       hasMoved = y != this.pY;
-                                       distance = Math.abs(input.deltaY);
-                               }
-                       }
-                       input.direction = direction;
-                       return hasMoved && distance > options.threshold && direction & options.direction;
-               },
-
-               attrTest: function(input) {
-                       return AttrRecognizer.prototype.attrTest.call(this, input) &&
-                               (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
-               },
-
-               emit: function(input) {
-
-                       this.pX = input.deltaX;
-                       this.pY = input.deltaY;
-
-                       var direction = directionStr(input.direction);
-
-                       if (direction) {
-                               input.additionalEvent = this.options.event + direction;
-                       }
-                       this._super.emit.call(this, input);
-               }
-       });
-
-       /**
-        * Pinch
-        * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
-        * @constructor
-        * @extends AttrRecognizer
-        */
-       function PinchRecognizer() {
-               AttrRecognizer.apply(this, arguments);
-       }
-
-       inherit(PinchRecognizer, AttrRecognizer, {
-               /**
-                * @namespace
-                * @memberof PinchRecognizer
-                */
-               defaults: {
-                       event: 'pinch',
-                       threshold: 0,
-                       pointers: 2
-               },
-
-               getTouchAction: function() {
-                       return [TOUCH_ACTION_NONE];
-               },
-
-               attrTest: function(input) {
-                       return this._super.attrTest.call(this, input) &&
-                               (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
-               },
-
-               emit: function(input) {
-                       if (input.scale !== 1) {
-                               var inOut = input.scale < 1 ? 'in' : 'out';
-                               input.additionalEvent = this.options.event + inOut;
-                       }
-                       this._super.emit.call(this, input);
-               }
-       });
-
-       /**
-        * Press
-        * Recognized when the pointer is down for x ms without any movement.
-        * @constructor
-        * @extends Recognizer
-        */
-       function PressRecognizer() {
-               Recognizer.apply(this, arguments);
-
-               this._timer = null;
-               this._input = null;
-       }
-
-       inherit(PressRecognizer, Recognizer, {
-               /**
-                * @namespace
-                * @memberof PressRecognizer
-                */
-               defaults: {
-                       event: 'press',
-                       pointers: 1,
-                       time: 251, // minimal time of the pointer to be pressed
-                       threshold: 9 // a minimal movement is ok, but keep it low
-               },
-
-               getTouchAction: function() {
-                       return [TOUCH_ACTION_AUTO];
-               },
-
-               process: function(input) {
-                       var options = this.options;
-                       var validPointers = input.pointers.length === options.pointers;
-                       var validMovement = input.distance < options.threshold;
-                       var validTime = input.deltaTime > options.time;
-
-                       this._input = input;
-
-                       // we only allow little movement
-                       // and we've reached an end event, so a tap is possible
-                       if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
-                               this.reset();
-                       } else if (input.eventType & INPUT_START) {
-                               this.reset();
-                               this._timer = setTimeoutContext(function() {
-                                       this.state = STATE_RECOGNIZED;
-                                       this.tryEmit();
-                               }, options.time, this);
-                       } else if (input.eventType & INPUT_END) {
-                               return STATE_RECOGNIZED;
-                       }
-                       return STATE_FAILED;
-               },
-
-               reset: function() {
-                       clearTimeout(this._timer);
-               },
-
-               emit: function(input) {
-                       if (this.state !== STATE_RECOGNIZED) {
-                               return;
-                       }
-
-                       if (input && (input.eventType & INPUT_END)) {
-                               this.manager.emit(this.options.event + 'up', input);
-                       } else {
-                               this._input.timeStamp = now();
-                               this.manager.emit(this.options.event, this._input);
-                       }
-               }
-       });
-
-       /**
-        * Rotate
-        * Recognized when two or more pointer are moving in a circular motion.
-        * @constructor
-        * @extends AttrRecognizer
-        */
-       function RotateRecognizer() {
-               AttrRecognizer.apply(this, arguments);
-       }
-
-       inherit(RotateRecognizer, AttrRecognizer, {
-               /**
-                * @namespace
-                * @memberof RotateRecognizer
-                */
-               defaults: {
-                       event: 'rotate',
-                       threshold: 0,
-                       pointers: 2
-               },
-
-               getTouchAction: function() {
-                       return [TOUCH_ACTION_NONE];
-               },
-
-               attrTest: function(input) {
-                       return this._super.attrTest.call(this, input) &&
-                               (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
-               }
-       });
-
-       /**
-        * Swipe
-        * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
-        * @constructor
-        * @extends AttrRecognizer
-        */
-       function SwipeRecognizer() {
-               AttrRecognizer.apply(this, arguments);
-       }
-
-       inherit(SwipeRecognizer, AttrRecognizer, {
-               /**
-                * @namespace
-                * @memberof SwipeRecognizer
-                */
-               defaults: {
-                       event: 'swipe',
-                       threshold: 10,
-                       velocity: 0.3,
-                       direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
-                       pointers: 1
-               },
-
-               getTouchAction: function() {
-                       return PanRecognizer.prototype.getTouchAction.call(this);
-               },
-
-               attrTest: function(input) {
-                       var direction = this.options.direction;
-                       var velocity;
-
-                       if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
-                               velocity = input.overallVelocity;
-                       } else if (direction & DIRECTION_HORIZONTAL) {
-                               velocity = input.overallVelocityX;
-                       } else if (direction & DIRECTION_VERTICAL) {
-                               velocity = input.overallVelocityY;
-                       }
-
-                       return this._super.attrTest.call(this, input) &&
-                               direction & input.offsetDirection &&
-                               input.distance > this.options.threshold &&
-                               input.maxPointers == this.options.pointers &&
-                               abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
-               },
-
-               emit: function(input) {
-                       var direction = directionStr(input.offsetDirection);
-                       if (direction) {
-                               this.manager.emit(this.options.event + direction, input);
-                       }
-
-                       this.manager.emit(this.options.event, input);
-               }
-       });
-
-       /**
-        * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
-        * between the given interval and position. The delay option can be used to recognize multi-taps without firing
-        * a single tap.
-        *
-        * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
-        * multi-taps being recognized.
-        * @constructor
-        * @extends Recognizer
-        */
-       function TapRecognizer() {
-               Recognizer.apply(this, arguments);
-
-               // previous time and center,
-               // used for tap counting
-               this.pTime = false;
-               this.pCenter = false;
-
-               this._timer = null;
-               this._input = null;
-               this.count = 0;
-       }
-
-       inherit(TapRecognizer, Recognizer, {
-               /**
-                * @namespace
-                * @memberof PinchRecognizer
-                */
-               defaults: {
-                       event: 'tap',
-                       pointers: 1,
-                       taps: 1,
-                       interval: 300, // max time between the multi-tap taps
-                       time: 250, // max time of the pointer to be down (like finger on the screen)
-                       threshold: 9, // a minimal movement is ok, but keep it low
-                       posThreshold: 10 // a multi-tap can be a bit off the initial position
-               },
-
-               getTouchAction: function() {
-                       return [TOUCH_ACTION_MANIPULATION];
-               },
-
-               process: function(input) {
-                       var options = this.options;
-
-                       var validPointers = input.pointers.length === options.pointers;
-                       var validMovement = input.distance < options.threshold;
-                       var validTouchTime = input.deltaTime < options.time;
-
-                       this.reset();
-
-                       if ((input.eventType & INPUT_START) && (this.count === 0)) {
-                               return this.failTimeout();
-                       }
-
-                       // we only allow little movement
-                       // and we've reached an end event, so a tap is possible
-                       if (validMovement && validTouchTime && validPointers) {
-                               if (input.eventType != INPUT_END) {
-                                       return this.failTimeout();
-                               }
-
-                               var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
-                               var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;
-
-                               this.pTime = input.timeStamp;
-                               this.pCenter = input.center;
-
-                               if (!validMultiTap || !validInterval) {
-                                       this.count = 1;
-                               } else {
-                                       this.count += 1;
-                               }
-
-                               this._input = input;
-
-                               // if tap count matches we have recognized it,
-                               // else it has began recognizing...
-                               var tapCount = this.count % options.taps;
-                               if (tapCount === 0) {
-                                       // no failing requirements, immediately trigger the tap event
-                                       // or wait as long as the multitap interval to trigger
-                                       if (!this.hasRequireFailures()) {
-                                               return STATE_RECOGNIZED;
-                                       } else {
-                                               this._timer = setTimeoutContext(function() {
-                                                       this.state = STATE_RECOGNIZED;
-                                                       this.tryEmit();
-                                               }, options.interval, this);
-                                               return STATE_BEGAN;
-                                       }
-                               }
-                       }
-                       return STATE_FAILED;
-               },
-
-               failTimeout: function() {
-                       this._timer = setTimeoutContext(function() {
-                               this.state = STATE_FAILED;
-                       }, this.options.interval, this);
-                       return STATE_FAILED;
-               },
-
-               reset: function() {
-                       clearTimeout(this._timer);
-               },
-
-               emit: function() {
-                       if (this.state == STATE_RECOGNIZED) {
-                               this._input.tapCount = this.count;
-                               this.manager.emit(this.options.event, this._input);
-                       }
-               }
-       });
-
-       /**
-        * Simple way to create a manager with a default set of recognizers.
-        * @param {HTMLElement} element
-        * @param {Object} [options]
-        * @constructor
-        */
-       function Hammer(element, options) {
-               options = options || {};
-               options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
-               return new Manager(element, options);
-       }
-
-       /**
-        * @const {string}
-        */
-       Hammer.VERSION = '2.0.8';
-
-       /**
-        * default settings
-        * @namespace
-        */
-       Hammer.defaults = {
-               /**
-                * set if DOM events are being triggered.
-                * But this is slower and unused by simple implementations, so disabled by default.
-                * @type {Boolean}
-                * @default false
-                */
-               domEvents: false,
-
-               /**
-                * The value for the touchAction property/fallback.
-                * When set to `compute` it will magically set the correct value based on the added recognizers.
-                * @type {String}
-                * @default compute
-                */
-               touchAction: TOUCH_ACTION_COMPUTE,
-
-               /**
-                * @type {Boolean}
-                * @default true
-                */
-               enable: true,
-
-               /**
-                * EXPERIMENTAL FEATURE -- can be removed/changed
-                * Change the parent input target element.
-                * If Null, then it is being set the to main element.
-                * @type {Null|EventTarget}
-                * @default null
-                */
-               inputTarget: null,
-
-               /**
-                * force an input class
-                * @type {Null|Function}
-                * @default null
-                */
-               inputClass: null,
-
-               /**
-                * Default recognizer setup when calling `Hammer()`
-                * When creating a new Manager these will be skipped.
-                * @type {Array}
-                */
-               preset: [
-                       // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
-                       [RotateRecognizer, {enable: false}],
-                       [PinchRecognizer, {enable: false}, ['rotate']],
-                       [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
-                       [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
-                       [TapRecognizer],
-                       [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
-                       [PressRecognizer]
-               ],
-
-               /**
-                * Some CSS properties can be used to improve the working of Hammer.
-                * Add them to this method and they will be set when creating a new Manager.
-                * @namespace
-                */
-               cssProps: {
-                       /**
-                        * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
-                        * @type {String}
-                        * @default 'none'
-                        */
-                       userSelect: 'none',
-
-                       /**
-                        * Disable the Windows Phone grippers when pressing an element.
-                        * @type {String}
-                        * @default 'none'
-                        */
-                       touchSelect: 'none',
-
-                       /**
-                        * Disables the default callout shown when you touch and hold a touch target.
-                        * On iOS, when you touch and hold a touch target such as a link, Safari displays
-                        * a callout containing information about the link. This property allows you to disable that callout.
-                        * @type {String}
-                        * @default 'none'
-                        */
-                       touchCallout: 'none',
-
-                       /**
-                        * Specifies whether zooming is enabled. Used by IE10>
-                        * @type {String}
-                        * @default 'none'
-                        */
-                       contentZooming: 'none',
-
-                       /**
-                        * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
-                        * @type {String}
-                        * @default 'none'
-                        */
-                       userDrag: 'none',
-
-                       /**
-                        * Overrides the highlight color shown when the user taps a link or a JavaScript
-                        * clickable element in iOS. This property obeys the alpha value, if specified.
-                        * @type {String}
-                        * @default 'rgba(0,0,0,0)'
-                        */
-                       tapHighlightColor: 'rgba(0,0,0,0)'
-               }
-       };
-
-       var STOP = 1;
-       var FORCED_STOP = 2;
-
-       /**
-        * Manager
-        * @param {HTMLElement} element
-        * @param {Object} [options]
-        * @constructor
-        */
-       function Manager(element, options) {
-               this.options = assign({}, Hammer.defaults, options || {});
-
-               this.options.inputTarget = this.options.inputTarget || element;
-
-               this.handlers = {};
-               this.session = {};
-               this.recognizers = [];
-               this.oldCssProps = {};
-
-               this.element = element;
-               this.input = createInputInstance(this);
-               this.touchAction = new TouchAction(this, this.options.touchAction);
-
-               toggleCssProps(this, true);
-
-               each(this.options.recognizers, function(item) {
-                       var recognizer = this.add(new (item[0])(item[1]));
-                       item[2] && recognizer.recognizeWith(item[2]);
-                       item[3] && recognizer.requireFailure(item[3]);
-               }, this);
-       }
-
-       Manager.prototype = {
-               /**
-                * set options
-                * @param {Object} options
-                * @returns {Manager}
-                */
-               set: function(options) {
-                       assign(this.options, options);
-
-                       // Options that need a little more setup
-                       if (options.touchAction) {
-                               this.touchAction.update();
-                       }
-                       if (options.inputTarget) {
-                               // Clean up existing event listeners and reinitialize
-                               this.input.destroy();
-                               this.input.target = options.inputTarget;
-                               this.input.init();
-                       }
-                       return this;
-               },
-
-               /**
-                * stop recognizing for this session.
-                * This session will be discarded, when a new [input]start event is fired.
-                * When forced, the recognizer cycle is stopped immediately.
-                * @param {Boolean} [force]
-                */
-               stop: function(force) {
-                       this.session.stopped = force ? FORCED_STOP : STOP;
-               },
-
-               /**
-                * run the recognizers!
-                * called by the inputHandler function on every movement of the pointers (touches)
-                * it walks through all the recognizers and tries to detect the gesture that is being made
-                * @param {Object} inputData
-                */
-               recognize: function(inputData) {
-                       var session = this.session;
-                       if (session.stopped) {
-                               return;
-                       }
-
-                       // run the touch-action polyfill
-                       this.touchAction.preventDefaults(inputData);
-
-                       var recognizer;
-                       var recognizers = this.recognizers;
-
-                       // this holds the recognizer that is being recognized.
-                       // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
-                       // if no recognizer is detecting a thing, it is set to `null`
-                       var curRecognizer = session.curRecognizer;
-
-                       // reset when the last recognizer is recognized
-                       // or when we're in a new session
-                       if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
-                               curRecognizer = session.curRecognizer = null;
-                       }
-
-                       var i = 0;
-                       while (i < recognizers.length) {
-                               recognizer = recognizers[i];
-
-                               // find out if we are allowed try to recognize the input for this one.
-                               // 1.   allow if the session is NOT forced stopped (see the .stop() method)
-                               // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
-                               //      that is being recognized.
-                               // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
-                               //      this can be setup with the `recognizeWith()` method on the recognizer.
-                               if (session.stopped !== FORCED_STOP && ( // 1
-                                       !curRecognizer || recognizer == curRecognizer || // 2
-                                       recognizer.canRecognizeWith(curRecognizer))) { // 3
-                                       recognizer.recognize(inputData);
-                               } else {
-                                       recognizer.reset();
-                               }
-
-                               // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
-                               // current active recognizer. but only if we don't already have an active recognizer
-                               if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
-                                       curRecognizer = session.curRecognizer = recognizer;
-                               }
-                               i++;
-                       }
-               },
-
-               /**
-                * get a recognizer by its event name.
-                * @param {Recognizer|String} recognizer
-                * @returns {Recognizer|Null}
-                */
-               get: function(recognizer) {
-                       if (recognizer instanceof Recognizer) {
-                               return recognizer;
-                       }
-
-                       var recognizers = this.recognizers;
-                       for (var i = 0; i < recognizers.length; i++) {
-                               if (recognizers[i].options.event == recognizer) {
-                                       return recognizers[i];
-                               }
-                       }
-                       return null;
-               },
-
-               /**
-                * add a recognizer to the manager
-                * existing recognizers with the same event name will be removed
-                * @param {Recognizer} recognizer
-                * @returns {Recognizer|Manager}
-                */
-               add: function(recognizer) {
-                       if (invokeArrayArg(recognizer, 'add', this)) {
-                               return this;
-                       }
-
-                       // remove existing
-                       var existing = this.get(recognizer.options.event);
-                       if (existing) {
-                               this.remove(existing);
-                       }
-
-                       this.recognizers.push(recognizer);
-                       recognizer.manager = this;
-
-                       this.touchAction.update();
-                       return recognizer;
-               },
-
-               /**
-                * remove a recognizer by name or instance
-                * @param {Recognizer|String} recognizer
-                * @returns {Manager}
-                */
-               remove: function(recognizer) {
-                       if (invokeArrayArg(recognizer, 'remove', this)) {
-                               return this;
-                       }
-
-                       recognizer = this.get(recognizer);
-
-                       // let's make sure this recognizer exists
-                       if (recognizer) {
-                               var recognizers = this.recognizers;
-                               var index = inArray(recognizers, recognizer);
-
-                               if (index !== -1) {
-                                       recognizers.splice(index, 1);
-                                       this.touchAction.update();
-                               }
-                       }
-
-                       return this;
-               },
-
-               /**
-                * bind event
-                * @param {String} events
-                * @param {Function} handler
-                * @returns {EventEmitter} this
-                */
-               on: function(events, handler) {
-                       if (events === undefined) {
-                               return;
-                       }
-                       if (handler === undefined) {
-                               return;
-                       }
-
-                       var handlers = this.handlers;
-                       each(splitStr(events), function(event) {
-                               handlers[event] = handlers[event] || [];
-                               handlers[event].push(handler);
-                       });
-                       return this;
-               },
-
-               /**
-                * unbind event, leave emit blank to remove all handlers
-                * @param {String} events
-                * @param {Function} [handler]
-                * @returns {EventEmitter} this
-                */
-               off: function(events, handler) {
-                       if (events === undefined) {
-                               return;
-                       }
-
-                       var handlers = this.handlers;
-                       each(splitStr(events), function(event) {
-                               if (!handler) {
-                                       delete handlers[event];
-                               } else {
-                                       handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
-                               }
-                       });
-                       return this;
-               },
-
-               /**
-                * emit event to the listeners
-                * @param {String} event
-                * @param {Object} data
-                */
-               emit: function(event, data) {
-                       // we also want to trigger dom events
-                       if (this.options.domEvents) {
-                               triggerDomEvent(event, data);
-                       }
-
-                       // no handlers, so skip it all
-                       var handlers = this.handlers[event] && this.handlers[event].slice();
-                       if (!handlers || !handlers.length) {
-                               return;
-                       }
-
-                       data.type = event;
-                       data.preventDefault = function() {
-                               data.srcEvent.preventDefault();
-                       };
-
-                       var i = 0;
-                       while (i < handlers.length) {
-                               handlers[i](data);
-                               i++;
-                       }
-               },
-
-               /**
-                * destroy the manager and unbinds all events
-                * it doesn't unbind dom events, that is the user own responsibility
-                */
-               destroy: function() {
-                       this.element && toggleCssProps(this, false);
-
-                       this.handlers = {};
-                       this.session = {};
-                       this.input.destroy();
-                       this.element = null;
-               }
-       };
-
-       /**
-        * add/remove the css properties as defined in manager.options.cssProps
-        * @param {Manager} manager
-        * @param {Boolean} add
-        */
-       function toggleCssProps(manager, add) {
-               var element = manager.element;
-               if (!element.style) {
-                       return;
-               }
-               var prop;
-               each(manager.options.cssProps, function(value, name) {
-                       prop = prefixed(element.style, name);
-                       if (add) {
-                               manager.oldCssProps[prop] = element.style[prop];
-                               element.style[prop] = value;
-                       } else {
-                               element.style[prop] = manager.oldCssProps[prop] || '';
-                       }
-               });
-               if (!add) {
-                       manager.oldCssProps = {};
-               }
-       }
-
-       /**
-        * trigger dom event
-        * @param {String} event
-        * @param {Object} data
-        */
-       function triggerDomEvent(event, data) {
-               var gestureEvent = document.createEvent('Event');
-               gestureEvent.initEvent(event, true, true);
-               gestureEvent.gesture = data;
-               data.target.dispatchEvent(gestureEvent);
-       }
-
-       assign(Hammer, {
-               INPUT_START: INPUT_START,
-               INPUT_MOVE: INPUT_MOVE,
-               INPUT_END: INPUT_END,
-               INPUT_CANCEL: INPUT_CANCEL,
-
-               STATE_POSSIBLE: STATE_POSSIBLE,
-               STATE_BEGAN: STATE_BEGAN,
-               STATE_CHANGED: STATE_CHANGED,
-               STATE_ENDED: STATE_ENDED,
-               STATE_RECOGNIZED: STATE_RECOGNIZED,
-               STATE_CANCELLED: STATE_CANCELLED,
-               STATE_FAILED: STATE_FAILED,
-
-               DIRECTION_NONE: DIRECTION_NONE,
-               DIRECTION_LEFT: DIRECTION_LEFT,
-               DIRECTION_RIGHT: DIRECTION_RIGHT,
-               DIRECTION_UP: DIRECTION_UP,
-               DIRECTION_DOWN: DIRECTION_DOWN,
-               DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
-               DIRECTION_VERTICAL: DIRECTION_VERTICAL,
-               DIRECTION_ALL: DIRECTION_ALL,
-
-               Manager: Manager,
-               Input: Input,
-               TouchAction: TouchAction,
-
-               TouchInput: TouchInput,
-               MouseInput: MouseInput,
-               PointerEventInput: PointerEventInput,
-               TouchMouseInput: TouchMouseInput,
-               SingleTouchInput: SingleTouchInput,
-
-               Recognizer: Recognizer,
-               AttrRecognizer: AttrRecognizer,
-               Tap: TapRecognizer,
-               Pan: PanRecognizer,
-               Swipe: SwipeRecognizer,
-               Pinch: PinchRecognizer,
-               Rotate: RotateRecognizer,
-               Press: PressRecognizer,
-
-               on: addEventListeners,
-               off: removeEventListeners,
-               each: each,
-               merge: merge,
-               extend: extend,
-               assign: assign,
-               inherit: inherit,
-               bindFn: bindFn,
-               prefixed: prefixed
-       });
-
-// this prevents errors when Hammer is loaded in the presence of an AMD
-//  style loader but by script tag, not by the loader.
-       var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
-       freeGlobal.Hammer = Hammer;
-
-       if (typeof define === 'function' && define.amd) {
-               define(function() {
-                       return Hammer;
-               });
-       } else if (typeof module != 'undefined' && module.exports) {
-               module.exports = Hammer;
-       } else {
-               window[exportName] = Hammer;
-       }
-
-})(window, document, 'Hammer');