hammer.js 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
  1. /*! Hammer.JS - v1.0.11 - 2014-05-20
  2. * http://eightmedia.github.io/hammer.js
  3. *
  4. * Copyright (c) 2014 Jorik Tangelder <j.tangelder@gmail.com>;
  5. * Licensed under the MIT license */
  6. (function(window, undefined) {
  7. 'use strict';
  8. /**
  9. * Hammer
  10. * use this to create instances
  11. * @param {HTMLElement} element
  12. * @param {Object} options
  13. * @returns {Hammer.Instance}
  14. * @constructor
  15. */
  16. var Hammer = function(element, options) {
  17. return new Hammer.Instance(element, options || {});
  18. };
  19. Hammer.VERSION = '1.0.11';
  20. // default settings
  21. Hammer.defaults = {
  22. // add styles and attributes to the element to prevent the browser from doing
  23. // its native behavior. this doesnt prevent the scrolling, but cancels
  24. // the contextmenu, tap highlighting etc
  25. // set to false to disable this
  26. stop_browser_behavior: {
  27. // this also triggers onselectstart=false for IE
  28. userSelect : 'none',
  29. // this makes the element blocking in IE10> and Chrome 35>, you could experiment with the value
  30. // see for more options the wiki: https://github.com/EightMedia/hammer.js/wiki
  31. touchAction : 'pan-y',
  32. touchCallout : 'none',
  33. contentZooming : 'none',
  34. userDrag : 'none',
  35. tapHighlightColor: 'rgba(0,0,0,0)'
  36. }
  37. //
  38. // more settings are defined per gesture at /gestures
  39. //
  40. };
  41. // detect touchevents
  42. Hammer.HAS_POINTEREVENTS = window.navigator.pointerEnabled || window.navigator.msPointerEnabled;
  43. Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);
  44. // dont use mouseevents on mobile devices
  45. Hammer.MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android|silk/i;
  46. Hammer.NO_MOUSEEVENTS = Hammer.HAS_TOUCHEVENTS && window.navigator.userAgent.match(Hammer.MOBILE_REGEX);
  47. // eventtypes per touchevent (start, move, end)
  48. // are filled by Event.determineEventTypes on setup
  49. Hammer.EVENT_TYPES = {};
  50. // interval in which Hammer recalculates current velocity in ms
  51. Hammer.UPDATE_VELOCITY_INTERVAL = 16;
  52. // hammer document where the base events are added at
  53. Hammer.DOCUMENT = window.document;
  54. // define these also as vars, for better minification
  55. // direction defines
  56. var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down';
  57. var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left';
  58. var DIRECTION_UP = Hammer.DIRECTION_UP = 'up';
  59. var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right';
  60. // pointer type
  61. var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse';
  62. var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch';
  63. var POINTER_PEN = Hammer.POINTER_PEN = 'pen';
  64. // touch event defines
  65. var EVENT_START = Hammer.EVENT_START = 'start';
  66. var EVENT_MOVE = Hammer.EVENT_MOVE = 'move';
  67. var EVENT_END = Hammer.EVENT_END = 'end';
  68. // plugins and gestures namespaces
  69. Hammer.plugins = Hammer.plugins || {};
  70. Hammer.gestures = Hammer.gestures || {};
  71. // if the window events are set...
  72. Hammer.READY = false;
  73. /**
  74. * setup events to detect gestures on the document
  75. */
  76. function setup() {
  77. if(Hammer.READY) {
  78. return;
  79. }
  80. // find what eventtypes we add listeners to
  81. Event.determineEventTypes();
  82. // Register all gestures inside Hammer.gestures
  83. Utils.each(Hammer.gestures, function(gesture){
  84. Detection.register(gesture);
  85. });
  86. // Add touch events on the document
  87. Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect);
  88. Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect);
  89. // Hammer is ready...!
  90. Hammer.READY = true;
  91. }
  92. var Utils = Hammer.utils = {
  93. /**
  94. * extend method,
  95. * also used for cloning when dest is an empty object
  96. * @param {Object} dest
  97. * @param {Object} src
  98. * @parm {Boolean} merge do a merge
  99. * @returns {Object} dest
  100. */
  101. extend: function extend(dest, src, merge) {
  102. for(var key in src) {
  103. if(dest[key] !== undefined && merge) {
  104. continue;
  105. }
  106. dest[key] = src[key];
  107. }
  108. return dest;
  109. },
  110. /**
  111. * for each
  112. * @param obj
  113. * @param iterator
  114. */
  115. each: function each(obj, iterator, context) {
  116. var i, o;
  117. // native forEach on arrays
  118. if ('forEach' in obj) {
  119. obj.forEach(iterator, context);
  120. }
  121. // arrays
  122. else if(obj.length !== undefined) {
  123. for(i=-1; (o=obj[++i]);) {
  124. if (iterator.call(context, o, i, obj) === false) {
  125. return;
  126. }
  127. }
  128. }
  129. // objects
  130. else {
  131. for(i in obj) {
  132. if(obj.hasOwnProperty(i) &&
  133. iterator.call(context, obj[i], i, obj) === false) {
  134. return;
  135. }
  136. }
  137. }
  138. },
  139. /**
  140. * find if a string contains the needle
  141. * @param {String} src
  142. * @param {String} needle
  143. * @returns {Boolean} found
  144. */
  145. inStr: function inStr(src, needle) {
  146. return src.indexOf(needle) > -1;
  147. },
  148. /**
  149. * find if a node is in the given parent
  150. * used for event delegation tricks
  151. * @param {HTMLElement} node
  152. * @param {HTMLElement} parent
  153. * @returns {boolean} has_parent
  154. */
  155. hasParent: function hasParent(node, parent) {
  156. while(node) {
  157. if(node == parent) {
  158. return true;
  159. }
  160. node = node.parentNode;
  161. }
  162. return false;
  163. },
  164. /**
  165. * get the center of all the touches
  166. * @param {Array} touches
  167. * @returns {Object} center pageXY clientXY
  168. */
  169. getCenter: function getCenter(touches) {
  170. var pageX = []
  171. , pageY = []
  172. , clientX = []
  173. , clientY = []
  174. , min = Math.min
  175. , max = Math.max;
  176. // no need to loop when only one touch
  177. if(touches.length === 1) {
  178. return {
  179. pageX: touches[0].pageX,
  180. pageY: touches[0].pageY,
  181. clientX: touches[0].clientX,
  182. clientY: touches[0].clientY
  183. };
  184. }
  185. Utils.each(touches, function(touch) {
  186. pageX.push(touch.pageX);
  187. pageY.push(touch.pageY);
  188. clientX.push(touch.clientX);
  189. clientY.push(touch.clientY);
  190. });
  191. return {
  192. pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2,
  193. pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2,
  194. clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2,
  195. clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2
  196. };
  197. },
  198. /**
  199. * calculate the velocity between two points
  200. * @param {Number} delta_time
  201. * @param {Number} delta_x
  202. * @param {Number} delta_y
  203. * @returns {Object} velocity
  204. */
  205. getVelocity: function getVelocity(delta_time, delta_x, delta_y) {
  206. return {
  207. x: Math.abs(delta_x / delta_time) || 0,
  208. y: Math.abs(delta_y / delta_time) || 0
  209. };
  210. },
  211. /**
  212. * calculate the angle between two coordinates
  213. * @param {Touch} touch1
  214. * @param {Touch} touch2
  215. * @returns {Number} angle
  216. */
  217. getAngle: function getAngle(touch1, touch2) {
  218. var x = touch2.clientX - touch1.clientX
  219. , y = touch2.clientY - touch1.clientY;
  220. return Math.atan2(y, x) * 180 / Math.PI;
  221. },
  222. /**
  223. * angle to direction define
  224. * @param {Touch} touch1
  225. * @param {Touch} touch2
  226. * @returns {String} direction constant, like DIRECTION_LEFT
  227. */
  228. getDirection: function getDirection(touch1, touch2) {
  229. var x = Math.abs(touch1.clientX - touch2.clientX)
  230. , y = Math.abs(touch1.clientY - touch2.clientY);
  231. if(x >= y) {
  232. return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
  233. }
  234. return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN;
  235. },
  236. /**
  237. * calculate the distance between two touches
  238. * @param {Touch} touch1
  239. * @param {Touch} touch2
  240. * @returns {Number} distance
  241. */
  242. getDistance: function getDistance(touch1, touch2) {
  243. var x = touch2.clientX - touch1.clientX
  244. , y = touch2.clientY - touch1.clientY;
  245. return Math.sqrt((x * x) + (y * y));
  246. },
  247. /**
  248. * calculate the scale factor between two touchLists (fingers)
  249. * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
  250. * @param {Array} start
  251. * @param {Array} end
  252. * @returns {Number} scale
  253. */
  254. getScale: function getScale(start, end) {
  255. // need two fingers...
  256. if(start.length >= 2 && end.length >= 2) {
  257. return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]);
  258. }
  259. return 1;
  260. },
  261. /**
  262. * calculate the rotation degrees between two touchLists (fingers)
  263. * @param {Array} start
  264. * @param {Array} end
  265. * @returns {Number} rotation
  266. */
  267. getRotation: function getRotation(start, end) {
  268. // need two fingers
  269. if(start.length >= 2 && end.length >= 2) {
  270. return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]);
  271. }
  272. return 0;
  273. },
  274. /**
  275. * boolean if the direction is vertical
  276. * @param {String} direction
  277. * @returns {Boolean} is_vertical
  278. */
  279. isVertical: function isVertical(direction) {
  280. return direction == DIRECTION_UP || direction == DIRECTION_DOWN;
  281. },
  282. /**
  283. * toggle browser default behavior with css props
  284. * @param {HtmlElement} element
  285. * @param {Object} css_props
  286. * @param {Boolean} toggle
  287. */
  288. toggleDefaultBehavior: function toggleDefaultBehavior(element, css_props, toggle) {
  289. if(!css_props || !element || !element.style) {
  290. return;
  291. }
  292. // with css properties for modern browsers
  293. Utils.each(['webkit', 'moz', 'Moz', 'ms', 'o', ''], function setStyle(vendor) {
  294. Utils.each(css_props, function(value, prop) {
  295. // vender prefix at the property
  296. if(vendor) {
  297. prop = vendor + prop.substring(0, 1).toUpperCase() + prop.substring(1);
  298. }
  299. // set the style
  300. if(prop in element.style) {
  301. element.style[prop] = !toggle && value;
  302. }
  303. });
  304. });
  305. var false_fn = function(){ return false; };
  306. // also the disable onselectstart
  307. if(css_props.userSelect == 'none') {
  308. element.onselectstart = !toggle && false_fn;
  309. }
  310. // and disable ondragstart
  311. if(css_props.userDrag == 'none') {
  312. element.ondragstart = !toggle && false_fn;
  313. }
  314. }
  315. };
  316. /**
  317. * create new hammer instance
  318. * all methods should return the instance itself, so it is chainable.
  319. * @param {HTMLElement} element
  320. * @param {Object} [options={}]
  321. * @returns {Hammer.Instance}
  322. * @constructor
  323. */
  324. Hammer.Instance = function(element, options) {
  325. var self = this;
  326. // setup HammerJS window events and register all gestures
  327. // this also sets up the default options
  328. setup();
  329. this.element = element;
  330. // start/stop detection option
  331. this.enabled = true;
  332. // merge options
  333. this.options = Utils.extend(
  334. Utils.extend({}, Hammer.defaults),
  335. options || {});
  336. // add some css to the element to prevent the browser from doing its native behavoir
  337. if(this.options.stop_browser_behavior) {
  338. Utils.toggleDefaultBehavior(this.element, this.options.stop_browser_behavior, false);
  339. }
  340. // start detection on touchstart
  341. this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) {
  342. if(self.enabled) {
  343. Detection.startDetect(self, ev);
  344. }
  345. });
  346. // keep a list of user event handlers which needs to be removed when calling 'dispose'
  347. this.eventHandlers = [];
  348. // return instance
  349. return this;
  350. };
  351. Hammer.Instance.prototype = {
  352. /**
  353. * bind events to the instance
  354. * @param {String} gesture
  355. * @param {Function} handler
  356. * @returns {Hammer.Instance}
  357. */
  358. on: function onEvent(gesture, handler) {
  359. var gestures = gesture.split(' ');
  360. Utils.each(gestures, function(gesture) {
  361. this.element.addEventListener(gesture, handler, false);
  362. this.eventHandlers.push({ gesture: gesture, handler: handler });
  363. }, this);
  364. return this;
  365. },
  366. /**
  367. * unbind events to the instance
  368. * @param {String} gesture
  369. * @param {Function} handler
  370. * @returns {Hammer.Instance}
  371. */
  372. off: function offEvent(gesture, handler) {
  373. var gestures = gesture.split(' ')
  374. , i, eh;
  375. Utils.each(gestures, function(gesture) {
  376. this.element.removeEventListener(gesture, handler, false);
  377. // remove the event handler from the internal list
  378. for(i=-1; (eh=this.eventHandlers[++i]);) {
  379. if(eh.gesture === gesture && eh.handler === handler) {
  380. this.eventHandlers.splice(i, 1);
  381. }
  382. }
  383. }, this);
  384. return this;
  385. },
  386. /**
  387. * trigger gesture event
  388. * @param {String} gesture
  389. * @param {Object} [eventData]
  390. * @returns {Hammer.Instance}
  391. */
  392. trigger: function triggerEvent(gesture, eventData) {
  393. // optional
  394. if(!eventData) {
  395. eventData = {};
  396. }
  397. // create DOM event
  398. var event = Hammer.DOCUMENT.createEvent('Event');
  399. event.initEvent(gesture, true, true);
  400. event.gesture = eventData;
  401. // trigger on the target if it is in the instance element,
  402. // this is for event delegation tricks
  403. var element = this.element;
  404. if(Utils.hasParent(eventData.target, element)) {
  405. element = eventData.target;
  406. }
  407. element.dispatchEvent(event);
  408. return this;
  409. },
  410. /**
  411. * enable of disable hammer.js detection
  412. * @param {Boolean} state
  413. * @returns {Hammer.Instance}
  414. */
  415. enable: function enable(state) {
  416. this.enabled = state;
  417. return this;
  418. },
  419. /**
  420. * dispose this hammer instance
  421. * @returns {Hammer.Instance}
  422. */
  423. dispose: function dispose() {
  424. var i, eh;
  425. // undo all changes made by stop_browser_behavior
  426. if(this.options.stop_browser_behavior) {
  427. Utils.toggleDefaultBehavior(this.element, this.options.stop_browser_behavior, true);
  428. }
  429. // unbind all custom event handlers
  430. for(i=-1; (eh=this.eventHandlers[++i]);) {
  431. this.element.removeEventListener(eh.gesture, eh.handler, false);
  432. }
  433. this.eventHandlers = [];
  434. // unbind the start event listener
  435. Event.unbindDom(this.element, Hammer.EVENT_TYPES[EVENT_START], this.eventStartHandler);
  436. return null;
  437. }
  438. };
  439. /**
  440. * this holds the last move event,
  441. * used to fix empty touchend issue
  442. * see the onTouch event for an explanation
  443. * @type {Object}
  444. */
  445. var last_move_event = null;
  446. /**
  447. * when the mouse is hold down, this is true
  448. * @type {Boolean}
  449. */
  450. var should_detect = false;
  451. /**
  452. * when touch events have been fired, this is true
  453. * @type {Boolean}
  454. */
  455. var touch_triggered = false;
  456. var Event = Hammer.event = {
  457. /**
  458. * simple addEventListener
  459. * @param {HTMLElement} element
  460. * @param {String} type
  461. * @param {Function} handler
  462. */
  463. bindDom: function(element, type, handler) {
  464. var types = type.split(' ');
  465. Utils.each(types, function(type){
  466. element.addEventListener(type, handler, false);
  467. });
  468. },
  469. /**
  470. * simple removeEventListener
  471. * @param {HTMLElement} element
  472. * @param {String} type
  473. * @param {Function} handler
  474. */
  475. unbindDom: function(element, type, handler) {
  476. var types = type.split(' ');
  477. Utils.each(types, function(type){
  478. element.removeEventListener(type, handler, false);
  479. });
  480. },
  481. /**
  482. * touch events with mouse fallback
  483. * @param {HTMLElement} element
  484. * @param {String} eventType like EVENT_MOVE
  485. * @param {Function} handler
  486. */
  487. onTouch: function onTouch(element, eventType, handler) {
  488. var self = this;
  489. var bindDomOnTouch = function bindDomOnTouch(ev) {
  490. var srcEventType = ev.type.toLowerCase();
  491. // onmouseup, but when touchend has been fired we do nothing.
  492. // this is for touchdevices which also fire a mouseup on touchend
  493. if(Utils.inStr(srcEventType, 'mouse') && touch_triggered) {
  494. return;
  495. }
  496. // mousebutton must be down or a touch event
  497. else if(Utils.inStr(srcEventType, 'touch') || // touch events are always on screen
  498. Utils.inStr(srcEventType, 'pointerdown') || // pointerevents touch
  499. (Utils.inStr(srcEventType, 'mouse') && ev.which === 1) // mouse is pressed
  500. ) {
  501. should_detect = true;
  502. }
  503. // mouse isn't pressed
  504. else if(Utils.inStr(srcEventType, 'mouse') && !ev.which) {
  505. should_detect = false;
  506. }
  507. // we are in a touch event, set the touch triggered bool to true,
  508. // this for the conflicts that may occur on ios and android
  509. if(Utils.inStr(srcEventType, 'touch') || Utils.inStr(srcEventType, 'pointer')) {
  510. touch_triggered = true;
  511. }
  512. // count the total touches on the screen
  513. var count_touches = 0;
  514. // when touch has been triggered in this detection session
  515. // and we are now handling a mouse event, we stop that to prevent conflicts
  516. if(should_detect) {
  517. // update pointerevent
  518. if(Hammer.HAS_POINTEREVENTS && eventType != EVENT_END) {
  519. count_touches = PointerEvent.updatePointer(eventType, ev);
  520. }
  521. // touch
  522. else if(Utils.inStr(srcEventType, 'touch')) {
  523. count_touches = ev.touches.length;
  524. }
  525. // mouse
  526. else if(!touch_triggered) {
  527. count_touches = Utils.inStr(srcEventType, 'up') ? 0 : 1;
  528. }
  529. // if we are in a end event, but when we remove one touch and
  530. // we still have enough, set eventType to move
  531. if(count_touches > 0 && eventType == EVENT_END) {
  532. eventType = EVENT_MOVE;
  533. }
  534. // no touches, force the end event
  535. else if(!count_touches) {
  536. eventType = EVENT_END;
  537. }
  538. // store the last move event
  539. if(count_touches || last_move_event === null) {
  540. last_move_event = ev;
  541. }
  542. // trigger the handler
  543. handler.call(Detection, self.collectEventData(element, eventType,
  544. self.getTouchList(last_move_event, eventType),
  545. ev) );
  546. // remove pointerevent from list
  547. if(Hammer.HAS_POINTEREVENTS && eventType == EVENT_END) {
  548. count_touches = PointerEvent.updatePointer(eventType, ev);
  549. }
  550. }
  551. // on the end we reset everything
  552. if(!count_touches) {
  553. last_move_event = null;
  554. should_detect = false;
  555. touch_triggered = false;
  556. PointerEvent.reset();
  557. }
  558. };
  559. this.bindDom(element, Hammer.EVENT_TYPES[eventType], bindDomOnTouch);
  560. // return the bound function to be able to unbind it later
  561. return bindDomOnTouch;
  562. },
  563. /**
  564. * we have different events for each device/browser
  565. * determine what we need and set them in the Hammer.EVENT_TYPES constant
  566. */
  567. determineEventTypes: function determineEventTypes() {
  568. // determine the eventtype we want to set
  569. var types;
  570. // pointerEvents magic
  571. if(Hammer.HAS_POINTEREVENTS) {
  572. types = PointerEvent.getEvents();
  573. }
  574. // on Android, iOS, blackberry, windows mobile we dont want any mouseevents
  575. else if(Hammer.NO_MOUSEEVENTS) {
  576. types = [
  577. 'touchstart',
  578. 'touchmove',
  579. 'touchend touchcancel'];
  580. }
  581. // for non pointer events browsers and mixed browsers,
  582. // like chrome on windows8 touch laptop
  583. else {
  584. types = [
  585. 'touchstart mousedown',
  586. 'touchmove mousemove',
  587. 'touchend touchcancel mouseup'];
  588. }
  589. Hammer.EVENT_TYPES[EVENT_START] = types[0];
  590. Hammer.EVENT_TYPES[EVENT_MOVE] = types[1];
  591. Hammer.EVENT_TYPES[EVENT_END] = types[2];
  592. },
  593. /**
  594. * create touchlist depending on the event
  595. * @param {Object} ev
  596. * @param {String} eventType used by the fakemultitouch plugin
  597. */
  598. getTouchList: function getTouchList(ev/*, eventType*/) {
  599. // get the fake pointerEvent touchlist
  600. if(Hammer.HAS_POINTEREVENTS) {
  601. return PointerEvent.getTouchList();
  602. }
  603. // get the touchlist
  604. if(ev.touches) {
  605. return ev.touches;
  606. }
  607. // make fake touchlist from mouse position
  608. ev.identifier = 1;
  609. return [ev];
  610. },
  611. /**
  612. * collect event data for Hammer js
  613. * @param {HTMLElement} element
  614. * @param {String} eventType like EVENT_MOVE
  615. * @param {Object} eventData
  616. */
  617. collectEventData: function collectEventData(element, eventType, touches, ev) {
  618. // find out pointerType
  619. var pointerType = POINTER_TOUCH;
  620. if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) {
  621. pointerType = POINTER_MOUSE;
  622. }
  623. return {
  624. center : Utils.getCenter(touches),
  625. timeStamp : Date.now(),
  626. target : ev.target,
  627. touches : touches,
  628. eventType : eventType,
  629. pointerType: pointerType,
  630. srcEvent : ev,
  631. /**
  632. * prevent the browser default actions
  633. * mostly used to disable scrolling of the browser
  634. */
  635. preventDefault: function() {
  636. var srcEvent = this.srcEvent;
  637. srcEvent.preventManipulation && srcEvent.preventManipulation();
  638. srcEvent.preventDefault && srcEvent.preventDefault();
  639. },
  640. /**
  641. * stop bubbling the event up to its parents
  642. */
  643. stopPropagation: function() {
  644. this.srcEvent.stopPropagation();
  645. },
  646. /**
  647. * immediately stop gesture detection
  648. * might be useful after a swipe was detected
  649. * @return {*}
  650. */
  651. stopDetect: function() {
  652. return Detection.stopDetect();
  653. }
  654. };
  655. }
  656. };
  657. var PointerEvent = Hammer.PointerEvent = {
  658. /**
  659. * holds all pointers
  660. * @type {Object}
  661. */
  662. pointers: {},
  663. /**
  664. * get a list of pointers
  665. * @returns {Array} touchlist
  666. */
  667. getTouchList: function getTouchList() {
  668. var touchlist = [];
  669. // we can use forEach since pointerEvents only is in IE10
  670. Utils.each(this.pointers, function(pointer){
  671. touchlist.push(pointer);
  672. });
  673. return touchlist;
  674. },
  675. /**
  676. * update the position of a pointer
  677. * @param {String} type EVENT_END
  678. * @param {Object} pointerEvent
  679. */
  680. updatePointer: function updatePointer(type, pointerEvent) {
  681. if(type == EVENT_END) {
  682. delete this.pointers[pointerEvent.pointerId];
  683. }
  684. else {
  685. pointerEvent.identifier = pointerEvent.pointerId;
  686. this.pointers[pointerEvent.pointerId] = pointerEvent;
  687. }
  688. // it's save to use Object.keys, since pointerEvents are only in newer browsers
  689. return Object.keys(this.pointers).length;
  690. },
  691. /**
  692. * check if ev matches pointertype
  693. * @param {String} pointerType POINTER_MOUSE
  694. * @param {PointerEvent} ev
  695. */
  696. matchType: function matchType(pointerType, ev) {
  697. if(!ev.pointerType) {
  698. return false;
  699. }
  700. var pt = ev.pointerType
  701. , types = {};
  702. types[POINTER_MOUSE] = (pt === POINTER_MOUSE);
  703. types[POINTER_TOUCH] = (pt === POINTER_TOUCH);
  704. types[POINTER_PEN] = (pt === POINTER_PEN);
  705. return types[pointerType];
  706. },
  707. /**
  708. * get events
  709. */
  710. getEvents: function getEvents() {
  711. return [
  712. 'pointerdown MSPointerDown',
  713. 'pointermove MSPointerMove',
  714. 'pointerup pointercancel MSPointerUp MSPointerCancel'
  715. ];
  716. },
  717. /**
  718. * reset the list
  719. */
  720. reset: function resetList() {
  721. this.pointers = {};
  722. }
  723. };
  724. var Detection = Hammer.detection = {
  725. // contains all registred Hammer.gestures in the correct order
  726. gestures: [],
  727. // data of the current Hammer.gesture detection session
  728. current : null,
  729. // the previous Hammer.gesture session data
  730. // is a full clone of the previous gesture.current object
  731. previous: null,
  732. // when this becomes true, no gestures are fired
  733. stopped : false,
  734. /**
  735. * start Hammer.gesture detection
  736. * @param {Hammer.Instance} inst
  737. * @param {Object} eventData
  738. */
  739. startDetect: function startDetect(inst, eventData) {
  740. // already busy with a Hammer.gesture detection on an element
  741. if(this.current) {
  742. return;
  743. }
  744. this.stopped = false;
  745. // holds current session
  746. this.current = {
  747. inst : inst, // reference to HammerInstance we're working for
  748. startEvent : Utils.extend({}, eventData), // start eventData for distances, timing etc
  749. lastEvent : false, // last eventData
  750. lastVelocityEvent : false, // last eventData for velocity.
  751. velocity : false, // current velocity
  752. name : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
  753. };
  754. this.detect(eventData);
  755. },
  756. /**
  757. * Hammer.gesture detection
  758. * @param {Object} eventData
  759. */
  760. detect: function detect(eventData) {
  761. if(!this.current || this.stopped) {
  762. return;
  763. }
  764. // extend event data with calculations about scale, distance etc
  765. eventData = this.extendEventData(eventData);
  766. // hammer instance and instance options
  767. var inst = this.current.inst,
  768. inst_options = inst.options;
  769. // call Hammer.gesture handlers
  770. Utils.each(this.gestures, function triggerGesture(gesture) {
  771. // only when the instance options have enabled this gesture
  772. if(!this.stopped && inst_options[gesture.name] !== false && inst.enabled !== false ) {
  773. // if a handler returns false, we stop with the detection
  774. if(gesture.handler.call(gesture, eventData, inst) === false) {
  775. this.stopDetect();
  776. return false;
  777. }
  778. }
  779. }, this);
  780. // store as previous event event
  781. if(this.current) {
  782. this.current.lastEvent = eventData;
  783. }
  784. // end event, but not the last touch, so dont stop
  785. if(eventData.eventType == EVENT_END && !eventData.touches.length - 1) {
  786. this.stopDetect();
  787. }
  788. return eventData;
  789. },
  790. /**
  791. * clear the Hammer.gesture vars
  792. * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
  793. * to stop other Hammer.gestures from being fired
  794. */
  795. stopDetect: function stopDetect() {
  796. // clone current data to the store as the previous gesture
  797. // used for the double tap gesture, since this is an other gesture detect session
  798. this.previous = Utils.extend({}, this.current);
  799. // reset the current
  800. this.current = null;
  801. // stopped!
  802. this.stopped = true;
  803. },
  804. /**
  805. * calculate velocity
  806. * @param {Object} ev
  807. * @param {Number} delta_time
  808. * @param {Number} delta_x
  809. * @param {Number} delta_y
  810. */
  811. getVelocityData: function getVelocityData(ev, delta_time, delta_x, delta_y) {
  812. var cur = this.current
  813. , velocityEv = cur.lastVelocityEvent
  814. , velocity = cur.velocity;
  815. // calculate velocity every x ms
  816. if (velocityEv && ev.timeStamp - velocityEv.timeStamp > Hammer.UPDATE_VELOCITY_INTERVAL) {
  817. velocity = Utils.getVelocity(ev.timeStamp - velocityEv.timeStamp,
  818. ev.center.clientX - velocityEv.center.clientX,
  819. ev.center.clientY - velocityEv.center.clientY);
  820. cur.lastVelocityEvent = ev;
  821. }
  822. else if(!cur.velocity) {
  823. velocity = Utils.getVelocity(delta_time, delta_x, delta_y);
  824. cur.lastVelocityEvent = ev;
  825. }
  826. cur.velocity = velocity;
  827. ev.velocityX = velocity.x;
  828. ev.velocityY = velocity.y;
  829. },
  830. /**
  831. * calculate interim angle and direction
  832. * @param {Object} ev
  833. */
  834. getInterimData: function getInterimData(ev) {
  835. var lastEvent = this.current.lastEvent
  836. , angle
  837. , direction;
  838. // end events (e.g. dragend) don't have useful values for interimDirection & interimAngle
  839. // because the previous event has exactly the same coordinates
  840. // so for end events, take the previous values of interimDirection & interimAngle
  841. // instead of recalculating them and getting a spurious '0'
  842. if(ev.eventType == EVENT_END) {
  843. angle = lastEvent && lastEvent.interimAngle;
  844. direction = lastEvent && lastEvent.interimDirection;
  845. }
  846. else {
  847. angle = lastEvent && Utils.getAngle(lastEvent.center, ev.center);
  848. direction = lastEvent && Utils.getDirection(lastEvent.center, ev.center);
  849. }
  850. ev.interimAngle = angle;
  851. ev.interimDirection = direction;
  852. },
  853. /**
  854. * extend eventData for Hammer.gestures
  855. * @param {Object} evData
  856. * @returns {Object} evData
  857. */
  858. extendEventData: function extendEventData(ev) {
  859. var cur = this.current
  860. , startEv = cur.startEvent;
  861. // if the touches change, set the new touches over the startEvent touches
  862. // this because touchevents don't have all the touches on touchstart, or the
  863. // user must place his fingers at the EXACT same time on the screen, which is not realistic
  864. // but, sometimes it happens that both fingers are touching at the EXACT same time
  865. if(ev.touches.length != startEv.touches.length || ev.touches === startEv.touches) {
  866. // extend 1 level deep to get the touchlist with the touch objects
  867. startEv.touches = [];
  868. Utils.each(ev.touches, function(touch) {
  869. startEv.touches.push(Utils.extend({}, touch));
  870. });
  871. }
  872. var delta_time = ev.timeStamp - startEv.timeStamp
  873. , delta_x = ev.center.clientX - startEv.center.clientX
  874. , delta_y = ev.center.clientY - startEv.center.clientY;
  875. this.getVelocityData(ev, delta_time, delta_x, delta_y);
  876. this.getInterimData(ev);
  877. Utils.extend(ev, {
  878. startEvent: startEv,
  879. deltaTime : delta_time,
  880. deltaX : delta_x,
  881. deltaY : delta_y,
  882. distance : Utils.getDistance(startEv.center, ev.center),
  883. angle : Utils.getAngle(startEv.center, ev.center),
  884. direction : Utils.getDirection(startEv.center, ev.center),
  885. scale : Utils.getScale(startEv.touches, ev.touches),
  886. rotation : Utils.getRotation(startEv.touches, ev.touches)
  887. });
  888. return ev;
  889. },
  890. /**
  891. * register new gesture
  892. * @param {Object} gesture object, see gestures.js for documentation
  893. * @returns {Array} gestures
  894. */
  895. register: function register(gesture) {
  896. // add an enable gesture options if there is no given
  897. var options = gesture.defaults || {};
  898. if(options[gesture.name] === undefined) {
  899. options[gesture.name] = true;
  900. }
  901. // extend Hammer default options with the Hammer.gesture options
  902. Utils.extend(Hammer.defaults, options, true);
  903. // set its index
  904. gesture.index = gesture.index || 1000;
  905. // add Hammer.gesture to the list
  906. this.gestures.push(gesture);
  907. // sort the list by index
  908. this.gestures.sort(function(a, b) {
  909. if(a.index < b.index) { return -1; }
  910. if(a.index > b.index) { return 1; }
  911. return 0;
  912. });
  913. return this.gestures;
  914. }
  915. };
  916. /**
  917. * Drag
  918. * Move with x fingers (default 1) around on the page. Blocking the scrolling when
  919. * moving left and right is a good practice. When all the drag events are blocking
  920. * you disable scrolling on that area.
  921. * @events drag, drapleft, dragright, dragup, dragdown
  922. */
  923. Hammer.gestures.Drag = {
  924. name : 'drag',
  925. index : 50,
  926. defaults : {
  927. drag_min_distance : 10,
  928. // Set correct_for_drag_min_distance to true to make the starting point of the drag
  929. // be calculated from where the drag was triggered, not from where the touch started.
  930. // Useful to avoid a jerk-starting drag, which can make fine-adjustments
  931. // through dragging difficult, and be visually unappealing.
  932. correct_for_drag_min_distance: true,
  933. // set 0 for unlimited, but this can conflict with transform
  934. drag_max_touches : 1,
  935. // prevent default browser behavior when dragging occurs
  936. // be careful with it, it makes the element a blocking element
  937. // when you are using the drag gesture, it is a good practice to set this true
  938. drag_block_horizontal : false,
  939. drag_block_vertical : false,
  940. // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
  941. // It disallows vertical directions if the initial direction was horizontal, and vice versa.
  942. drag_lock_to_axis : false,
  943. // drag lock only kicks in when distance > drag_lock_min_distance
  944. // This way, locking occurs only when the distance has become large enough to reliably determine the direction
  945. drag_lock_min_distance : 25
  946. },
  947. triggered: false,
  948. handler : function dragGesture(ev, inst) {
  949. var cur = Detection.current;
  950. // current gesture isnt drag, but dragged is true
  951. // this means an other gesture is busy. now call dragend
  952. if(cur.name != this.name && this.triggered) {
  953. inst.trigger(this.name + 'end', ev);
  954. this.triggered = false;
  955. return;
  956. }
  957. // max touches
  958. if(inst.options.drag_max_touches > 0 &&
  959. ev.touches.length > inst.options.drag_max_touches) {
  960. return;
  961. }
  962. switch(ev.eventType) {
  963. case EVENT_START:
  964. this.triggered = false;
  965. break;
  966. case EVENT_MOVE:
  967. // when the distance we moved is too small we skip this gesture
  968. // or we can be already in dragging
  969. if(ev.distance < inst.options.drag_min_distance &&
  970. cur.name != this.name) {
  971. return;
  972. }
  973. var startCenter = cur.startEvent.center;
  974. // we are dragging!
  975. if(cur.name != this.name) {
  976. cur.name = this.name;
  977. if(inst.options.correct_for_drag_min_distance && ev.distance > 0) {
  978. // When a drag is triggered, set the event center to drag_min_distance pixels from the original event center.
  979. // Without this correction, the dragged distance would jumpstart at drag_min_distance pixels instead of at 0.
  980. // It might be useful to save the original start point somewhere
  981. var factor = Math.abs(inst.options.drag_min_distance / ev.distance);
  982. startCenter.pageX += ev.deltaX * factor;
  983. startCenter.pageY += ev.deltaY * factor;
  984. startCenter.clientX += ev.deltaX * factor;
  985. startCenter.clientY += ev.deltaY * factor;
  986. // recalculate event data using new start point
  987. ev = Detection.extendEventData(ev);
  988. }
  989. }
  990. // lock drag to axis?
  991. if(cur.lastEvent.drag_locked_to_axis ||
  992. ( inst.options.drag_lock_to_axis &&
  993. inst.options.drag_lock_min_distance <= ev.distance
  994. )) {
  995. ev.drag_locked_to_axis = true;
  996. }
  997. var last_direction = cur.lastEvent.direction;
  998. if(ev.drag_locked_to_axis && last_direction !== ev.direction) {
  999. // keep direction on the axis that the drag gesture started on
  1000. if(Utils.isVertical(last_direction)) {
  1001. ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN;
  1002. }
  1003. else {
  1004. ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
  1005. }
  1006. }
  1007. // first time, trigger dragstart event
  1008. if(!this.triggered) {
  1009. inst.trigger(this.name + 'start', ev);
  1010. this.triggered = true;
  1011. }
  1012. // trigger events
  1013. inst.trigger(this.name, ev);
  1014. inst.trigger(this.name + ev.direction, ev);
  1015. var is_vertical = Utils.isVertical(ev.direction);
  1016. // block the browser events
  1017. if((inst.options.drag_block_vertical && is_vertical) ||
  1018. (inst.options.drag_block_horizontal && !is_vertical)) {
  1019. ev.preventDefault();
  1020. }
  1021. break;
  1022. case EVENT_END:
  1023. // trigger dragend
  1024. if(this.triggered) {
  1025. inst.trigger(this.name + 'end', ev);
  1026. }
  1027. this.triggered = false;
  1028. break;
  1029. }
  1030. }
  1031. };
  1032. /**
  1033. * Hold
  1034. * Touch stays at the same place for x time
  1035. * @events hold
  1036. */
  1037. Hammer.gestures.Hold = {
  1038. name : 'hold',
  1039. index : 10,
  1040. defaults: {
  1041. hold_timeout : 500,
  1042. hold_threshold: 2
  1043. },
  1044. timer : null,
  1045. handler : function holdGesture(ev, inst) {
  1046. switch(ev.eventType) {
  1047. case EVENT_START:
  1048. // clear any running timers
  1049. clearTimeout(this.timer);
  1050. // set the gesture so we can check in the timeout if it still is
  1051. Detection.current.name = this.name;
  1052. // set timer and if after the timeout it still is hold,
  1053. // we trigger the hold event
  1054. this.timer = setTimeout(function() {
  1055. if(Detection.current.name == 'hold') {
  1056. inst.trigger('hold', ev);
  1057. }
  1058. }, inst.options.hold_timeout);
  1059. break;
  1060. // when you move or end we clear the timer
  1061. case EVENT_MOVE:
  1062. if(ev.distance > inst.options.hold_threshold) {
  1063. clearTimeout(this.timer);
  1064. }
  1065. break;
  1066. case EVENT_END:
  1067. clearTimeout(this.timer);
  1068. break;
  1069. }
  1070. }
  1071. };
  1072. /**
  1073. * Release
  1074. * Called as last, tells the user has released the screen
  1075. * @events release
  1076. */
  1077. Hammer.gestures.Release = {
  1078. name : 'release',
  1079. index : Infinity,
  1080. handler: function releaseGesture(ev, inst) {
  1081. if(ev.eventType == EVENT_END) {
  1082. inst.trigger(this.name, ev);
  1083. }
  1084. }
  1085. };
  1086. /**
  1087. * Swipe
  1088. * triggers swipe events when the end velocity is above the threshold
  1089. * for best usage, set prevent_default (on the drag gesture) to true
  1090. * @events swipe, swipeleft, swiperight, swipeup, swipedown
  1091. */
  1092. Hammer.gestures.Swipe = {
  1093. name : 'swipe',
  1094. index : 40,
  1095. defaults: {
  1096. swipe_min_touches: 1,
  1097. swipe_max_touches: 1,
  1098. swipe_velocity : 0.7
  1099. },
  1100. handler : function swipeGesture(ev, inst) {
  1101. if(ev.eventType == EVENT_END) {
  1102. // max touches
  1103. if(ev.touches.length < inst.options.swipe_min_touches ||
  1104. ev.touches.length > inst.options.swipe_max_touches) {
  1105. return;
  1106. }
  1107. // when the distance we moved is too small we skip this gesture
  1108. // or we can be already in dragging
  1109. if(ev.velocityX > inst.options.swipe_velocity ||
  1110. ev.velocityY > inst.options.swipe_velocity) {
  1111. // trigger swipe events
  1112. inst.trigger(this.name, ev);
  1113. inst.trigger(this.name + ev.direction, ev);
  1114. }
  1115. }
  1116. }
  1117. };
  1118. /**
  1119. * Tap/DoubleTap
  1120. * Quick touch at a place or double at the same place
  1121. * @events tap, doubletap
  1122. */
  1123. Hammer.gestures.Tap = {
  1124. name : 'tap',
  1125. index : 100,
  1126. defaults: {
  1127. tap_max_touchtime : 250,
  1128. tap_max_distance : 10,
  1129. tap_always : true,
  1130. doubletap_distance: 20,
  1131. doubletap_interval: 300
  1132. },
  1133. has_moved: false,
  1134. handler : function tapGesture(ev, inst) {
  1135. var prev, since_prev, did_doubletap;
  1136. // reset moved state
  1137. if(ev.eventType == EVENT_START) {
  1138. this.has_moved = false;
  1139. }
  1140. // Track the distance we've moved. If it's above the max ONCE, remember that (fixes #406).
  1141. else if(ev.eventType == EVENT_MOVE && !this.moved) {
  1142. this.has_moved = (ev.distance > inst.options.tap_max_distance);
  1143. }
  1144. else if(ev.eventType == EVENT_END &&
  1145. ev.srcEvent.type != 'touchcancel' &&
  1146. ev.deltaTime < inst.options.tap_max_touchtime && !this.has_moved) {
  1147. // previous gesture, for the double tap since these are two different gesture detections
  1148. prev = Detection.previous;
  1149. since_prev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp;
  1150. did_doubletap = false;
  1151. // check if double tap
  1152. if(prev && prev.name == 'tap' &&
  1153. (since_prev && since_prev < inst.options.doubletap_interval) &&
  1154. ev.distance < inst.options.doubletap_distance) {
  1155. inst.trigger('doubletap', ev);
  1156. did_doubletap = true;
  1157. }
  1158. // do a single tap
  1159. if(!did_doubletap || inst.options.tap_always) {
  1160. Detection.current.name = 'tap';
  1161. inst.trigger(Detection.current.name, ev);
  1162. }
  1163. }
  1164. }
  1165. };
  1166. /**
  1167. * Touch
  1168. * Called as first, tells the user has touched the screen
  1169. * @events touch
  1170. */
  1171. Hammer.gestures.Touch = {
  1172. name : 'touch',
  1173. index : -Infinity,
  1174. defaults: {
  1175. // call preventDefault at touchstart, and makes the element blocking by
  1176. // disabling the scrolling of the page, but it improves gestures like
  1177. // transforming and dragging.
  1178. // be careful with using this, it can be very annoying for users to be stuck
  1179. // on the page
  1180. prevent_default : false,
  1181. // disable mouse events, so only touch (or pen!) input triggers events
  1182. prevent_mouseevents: false
  1183. },
  1184. handler : function touchGesture(ev, inst) {
  1185. if(inst.options.prevent_mouseevents &&
  1186. ev.pointerType == POINTER_MOUSE) {
  1187. ev.stopDetect();
  1188. return;
  1189. }
  1190. if(inst.options.prevent_default) {
  1191. ev.preventDefault();
  1192. }
  1193. if(ev.eventType == EVENT_START) {
  1194. inst.trigger(this.name, ev);
  1195. }
  1196. }
  1197. };
  1198. /**
  1199. * Transform
  1200. * User want to scale or rotate with 2 fingers
  1201. * @events transform, pinch, pinchin, pinchout, rotate
  1202. */
  1203. Hammer.gestures.Transform = {
  1204. name : 'transform',
  1205. index : 45,
  1206. defaults : {
  1207. // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
  1208. transform_min_scale : 0.01,
  1209. // rotation in degrees
  1210. transform_min_rotation : 1,
  1211. // prevent default browser behavior when two touches are on the screen
  1212. // but it makes the element a blocking element
  1213. // when you are using the transform gesture, it is a good practice to set this true
  1214. transform_always_block : false,
  1215. // ensures that all touches occurred within the instance element
  1216. transform_within_instance: false
  1217. },
  1218. triggered: false,
  1219. handler : function transformGesture(ev, inst) {
  1220. // current gesture isnt drag, but dragged is true
  1221. // this means an other gesture is busy. now call dragend
  1222. if(Detection.current.name != this.name && this.triggered) {
  1223. inst.trigger(this.name + 'end', ev);
  1224. this.triggered = false;
  1225. return;
  1226. }
  1227. // at least multitouch
  1228. if(ev.touches.length < 2) {
  1229. return;
  1230. }
  1231. // prevent default when two fingers are on the screen
  1232. if(inst.options.transform_always_block) {
  1233. ev.preventDefault();
  1234. }
  1235. // check if all touches occurred within the instance element
  1236. if(inst.options.transform_within_instance) {
  1237. for(var i=-1; ev.touches[++i];) {
  1238. if(!Utils.hasParent(ev.touches[i].target, inst.element)) {
  1239. return;
  1240. }
  1241. }
  1242. }
  1243. switch(ev.eventType) {
  1244. case EVENT_START:
  1245. this.triggered = false;
  1246. break;
  1247. case EVENT_MOVE:
  1248. var scale_threshold = Math.abs(1 - ev.scale);
  1249. var rotation_threshold = Math.abs(ev.rotation);
  1250. // when the distance we moved is too small we skip this gesture
  1251. // or we can be already in dragging
  1252. if(scale_threshold < inst.options.transform_min_scale &&
  1253. rotation_threshold < inst.options.transform_min_rotation) {
  1254. return;
  1255. }
  1256. // we are transforming!
  1257. Detection.current.name = this.name;
  1258. // first time, trigger dragstart event
  1259. if(!this.triggered) {
  1260. inst.trigger(this.name + 'start', ev);
  1261. this.triggered = true;
  1262. }
  1263. inst.trigger(this.name, ev); // basic transform event
  1264. // trigger rotate event
  1265. if(rotation_threshold > inst.options.transform_min_rotation) {
  1266. inst.trigger('rotate', ev);
  1267. }
  1268. // trigger pinch event
  1269. if(scale_threshold > inst.options.transform_min_scale) {
  1270. inst.trigger('pinch', ev);
  1271. inst.trigger('pinch' + (ev.scale<1 ? 'in' : 'out'), ev);
  1272. }
  1273. break;
  1274. case EVENT_END:
  1275. // trigger dragend
  1276. if(this.triggered) {
  1277. inst.trigger(this.name + 'end', ev);
  1278. }
  1279. this.triggered = false;
  1280. break;
  1281. }
  1282. }
  1283. };
  1284. // AMD export
  1285. if(typeof define == 'function' && define.amd) {
  1286. define(function(){
  1287. return Hammer;
  1288. });
  1289. }
  1290. // commonjs export
  1291. else if(typeof module == 'object' && module.exports) {
  1292. module.exports = Hammer;
  1293. }
  1294. // browser export
  1295. else {
  1296. window.Hammer = Hammer;
  1297. }
  1298. })(window);