jquery.hammer.js 48 KB

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