mobile.js 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533
  1. /*jslint undef: true, browser: true, continue: true, eqeq: true, vars: true, forin: true, white: true, newcap: false, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
  2. /**
  3. * jGestures: a jQuery plugin for gesture events
  4. * Copyright 2010-2011 Neue Digitale / Razorfish GmbH
  5. * Copyright 2011-2012, Razorfish GmbH
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * @fileOverview
  17. * Razorfish GmbH javascript library: add touch events such as 'pinch',
  18. * 'rotate', 'swipe', 'tap' and 'orientationchange' on capable user agents.
  19. * For incapable devices there's a basic event substitution: a "tapone" event
  20. * can be triggered by "clicking", a "swipeone" by performing a swipe-ish
  21. * gesture using the mouse (buttondown - mousemove - buttonup).
  22. *
  23. * This is still a beta version, bugfixes and improvements appreciated.
  24. *
  25. * @author martin.krause@razorfish.de
  26. * @version 0.90-shake
  27. *
  28. * @requires
  29. * jQuery JavaScript Library v1.4.2 - http://jquery.com/
  30. * Copyright 2010, John Resig
  31. * Dual licensed under the MIT or GPL Version 2 licenses.
  32. * http://jquery.org/license
  33. *
  34. * @example jQuery('#swipe').bind('swipeone',eventHandler);
  35. *
  36. * Notification on native events:
  37. * On every native touchstart, touchend, gesturestart and gestureend-event,
  38. * jgestures triggers a corresponding custom event
  39. * ('jGestures.touchstart', 'jGestures.touchend;start', 'jGestures.touchend;processed',
  40. * 'jGestures.gesturestart', 'jGestures.gestureend;start', 'jGestures.gestureend;processed') on the event-element.
  41. * The eventhandler's second argument represents the original touch event (yes: including all touchpoints).
  42. * Use this if you need very detailed control e.g. kinetic scrolling or implementing additional gestures.
  43. *
  44. * Every jGesture-eventhandler receives a custom object as second argument
  45. * containing the original event (originalEvent property) and processed
  46. * information (such as delta values and timesptamp).
  47. * Example:{
  48. * type: eventtype e.g. "swipe","pinch",
  49. * originalEvent: {DOM-Event},
  50. * // default: just one entry on the delta-array - the first touchpoint
  51. * // the first touchpoint is the reference point for every gesture,
  52. * // because moving touchpoints in various directions would result in
  53. * // a gesture.
  54. * // delta and direction details are just provided for touch not for gesture / motion events
  55. * delta : [
  56. * {
  57. * lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
  58. * lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
  59. * moved: {Number}, // distance: relative to the original touchpoint
  60. * startX: {Number} , // relative to the original touchpoint
  61. * startY: {Number} ,// relative to the original touchpoint
  62. * } ],
  63. * // based on the first touchpoint
  64. * direction : { // relative to the last touchevent (e.g. touchmove!)
  65. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  66. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  67. * lastX : {Number}, // -1,0,+1 || null (orientationchange) // relative to the last touchevent (e.g. touchmove!)
  68. * lastY : {Number}, // -1,0,+1 || null (orientationchange)// relative to the last touchevent (e.g. touchmove!)
  69. * startX: {Number} , // relative to the original touchpoint
  70. * startY: {Number} ,// relative to the original touchpoint
  71. * },
  72. * rotation: {Number} || null, // gestureonly: amount of rotation relative to the current position NOT the original
  73. * scale: {Number} || null, // gestureonly: amount of scaling relative to the current position NOT the original
  74. * duration: {Number}, // ms: relative to the original touchpoint
  75. * description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
  76. * };
  77. *
  78. * Available jGesture-events can be grouped into:
  79. *
  80. *
  81. * Device events:
  82. * The jGesture-Events in this group are triggered by the device.
  83. *
  84. * @event 'orientationchange'
  85. * The device is turned clockwise or counterclockwise. This event is triggered
  86. * by the device and might use an internal gyroscope.
  87. * obj.description:
  88. * orientationchange:landscape:clockwise:-90
  89. * orientationchange:portrait:default:0
  90. * orientationchange:landscape:counterclockwise|portrait:90
  91. * orientationchange:portrait:upsidedown:180
  92. *
  93. *
  94. * Move events:
  95. * The jGesture-Events in this group are triggered during the touch/gesture
  96. * execution whenever a touchpoint changes.
  97. * In contrast to touchend/gestureend-events which are triggered after
  98. * the touch/gesture has completed.
  99. *
  100. * @event 'pinch'
  101. * Is triggered during a pinch gesture (two fingers moving away from or
  102. * towards each other).
  103. * obj.description:
  104. * pinch:-1:close
  105. * pinch:+1:open
  106. *
  107. * @event 'rotate'
  108. * Is triggered during a rotation gesture (two fingers rotating clockwise
  109. * or counterclockwise).
  110. * obj.description:
  111. * rotate:-1:counterclockwise
  112. * rotate:+1:+clockwise
  113. *
  114. * @event 'swipemove'
  115. * Is triggered during a swipe move gesture (finger(s) being moved around
  116. * the device, e.g. dragging)
  117. * obj.description:
  118. * swipemove:1:left:down
  119. * swipemove:1:left:up
  120. * swipemove:1:left:steady
  121. * swipemove:1:right:down
  122. * swipemove:1:right:up
  123. * swipemove:1:right:steady
  124. * swipemove:2:left:down
  125. * swipemove:2:left:up
  126. * swipemove:2:left:steady
  127. * swipemove:2:right:down
  128. * swipemove:2:right:up
  129. * swipemove:2:right:steady
  130. * swipemove:2:left:down
  131. * swipemove:3:left:up
  132. * swipemove:3:left:steady
  133. * swipemove:3:right:down
  134. * swipemove:3:right:up
  135. * swipemove:3:right:steady
  136. * swipemove:3:left:down
  137. * swipemove:4:left:up
  138. * swipemove:4:left:steady
  139. * swipemove:4:right:down
  140. * swipemove:4:right:up
  141. * swipemove:4:right:steady
  142. *
  143. *
  144. * Toucheend events:
  145. * The jGesture-Events in this group are triggered after the touch/gesture
  146. * has completed.
  147. * In contrast to touchmove-events which are triggered during the touch/gesture
  148. * execution whenever a touchpoint changes.
  149. *
  150. * @event 'swipeone'
  151. * Is triggered after a swipe move gesture with one touchpoint (one finger
  152. * was moved around the device)
  153. * obj.description:
  154. * swipeone:1:left:down
  155. * swipeone:1:left:up
  156. * swipeone:1:left:steady
  157. * swipeone:1:right:down
  158. * swipeone:1:right:up
  159. * swipeone:1:right:steady
  160. *
  161. * @event 'swipetwo'
  162. * Is triggered after a swipe move gesture with two touchpoints (two fingers
  163. * were moved around the device)
  164. * obj.description:
  165. * swipetwo:2:left:down
  166. * swipetwo:2:left:up
  167. * swipetwo:2:left:steady
  168. * swipetwo:2:right:down
  169. * swipetwo:2:right:up
  170. * swipetwo:2:right:steady
  171. *
  172. * @event 'swipethree'
  173. * Is triggered after a swipe move gesture with three touchpoints (three
  174. * fingers were moved around the device)
  175. * obj.description:
  176. * swipethree:3:left:down
  177. * swipethree:3:left:up
  178. * swipethree:3:left:steady
  179. * swipethree:3:right:down
  180. * swipethree:3:right:up
  181. * swipethree:3:right:steady
  182. *
  183. * @event 'swipefour'
  184. * Is triggered after a swipe move gesture with four touchpoints (four
  185. * fingers were moved around the device)
  186. * obj.description:
  187. * swipefour:4:left:down
  188. * swipefour:4:left:up
  189. * swipefour:4:left:steady
  190. * swipefour:4:right:down
  191. * swipefour:4:right:up
  192. * swipefour:4:right:steady
  193. *
  194. *
  195. * @event 'swipeup'
  196. * Is triggered after an strict upwards swipe move gesture
  197. * obj.description:
  198. * swipe:1:steady:up
  199. * swipe:2:steady:up
  200. * swipe:3:steady:up
  201. * swipe:4:steady:up
  202. *
  203. * @event 'swiperightup'
  204. * Is triggered after a rightwards and upwards swipe move gesture
  205. * obj.description:
  206. * swipe:1:right:up
  207. * swipe:2:right:up
  208. * swipe:3:right:up
  209. * swipe:4:right:up
  210. *
  211. * @event 'swiperight'
  212. * Is triggered after a strict rightwards swipe move gesture
  213. * obj.description:
  214. * swipe:1:right:steady
  215. * swipe:2:right:steady
  216. * swipe:3:right:steady
  217. * swipe:4:right:steady
  218. *
  219. * @event 'swiperightdown'
  220. * Is triggered after a rightwards and downwards swipe move gesture
  221. * obj.description:
  222. * swipe:1:right:down
  223. * swipe:2:right:down
  224. * swipe:3:right:down
  225. * swipe:4:right:down
  226. *
  227. * @event 'swipedown'
  228. * Is triggered after a strict downwards swipe move gesture
  229. * obj.description:
  230. * swipe:1:steady:down
  231. * swipe:2:steady:down
  232. * swipe:3:steady:down
  233. * swipe:4:steady:down
  234. *
  235. * @event 'swipeleftdown'
  236. * Is triggered after a leftwards and downwards swipe move gesture
  237. * obj.description:
  238. * swipe:1:left:down
  239. * swipe:2:left:down
  240. * swipe:3:left:down
  241. * swipe:4:left:down
  242. *
  243. * @event 'swipeleft'
  244. * Is triggered after a strict leftwards swipe move gesture
  245. * obj.description:
  246. * swipe:1:left:steady
  247. * swipe:2:left:steady
  248. * swipe:3:left:steady
  249. * swipe:4:left:steady
  250. *
  251. * @event 'swipeleftup'
  252. * Is triggered after a leftwards and upwards swipe move gesture
  253. * obj.description:
  254. * swipe:1:left:up
  255. * swipe:2:left:up
  256. * swipe:3:left:up
  257. * swipe:4:left:up
  258. *
  259. * @event 'tapone'
  260. * Is triggered after a single (one finger) tap gesture
  261. * obj.description:
  262. * tapone
  263. *
  264. * @event 'taptwo'
  265. * Is triggered after a double (two finger) tap gesture
  266. * obj.description:
  267. * taptwo
  268. * *
  269. * @event 'tapthree'
  270. * Is triggered after a tripple (three finger) tap gesture
  271. * obj.description:
  272. * tapthree
  273. *
  274. *
  275. * Gestureend events:
  276. * A gesture is an interpretation of different touchpoints.
  277. * The jGesture-Events in this group are triggered when a gesture has finished
  278. * and the touchpoints are removed from the device.
  279. *
  280. * @event 'pinchopen'
  281. * Is triggered when a pinchopen gesture (two fingers moving away from each
  282. * other) occured and the touchpoints (fingers) are removed the device.
  283. * obj.description:
  284. * pinch:+1:open
  285. *
  286. * @event 'pinchclose'
  287. * Is triggered when a pinchclose gesture (two fingers moving towards each
  288. * other) occured and the touchpoints (fingers) are removed the device.
  289. * obj.description:
  290. * pinch:-1:close
  291. *
  292. * @event 'rotatecw'
  293. * Is triggered when a clockwise rotation gesture (two fingers rotating
  294. * clockwise) occured and the touchpoints (fingers) are removed the device.
  295. * obj.description:
  296. * rotate:+1:+clockwise
  297. *
  298. * @event 'rotateccw'
  299. * Is triggered when a counterclockwise rotation gesture (two fingers
  300. * rotating counterclockwise) occured and the touchpoints (fingers) are
  301. * removed the device.
  302. * obj.description:
  303. * rotate:-1:+counterclockwise
  304. *
  305. *
  306. * Motion events:
  307. * A "motion event" is an interpretation of changes in space, e.g. a "shaking motion"
  308. * consists of a specified number of acceleration changes in a given interval.
  309. * For understanding "directions", place your mobile device on a table with the bottom
  310. * (home button) close to you:
  311. * - x-axis: horizontal left / right
  312. * - y-axis: horizontal front / back (through the home button)
  313. * - z-axis: vertical through your device
  314. *
  315. * Note: Devicemotion / deviceorientation don't send custom event (such as: jGestures.touchstart).
  316. * Note: Devicemotion should be bound on the "window-element" - because the whole device moves
  317. *
  318. * @event 'shake'
  319. * Is triggered when a shaking motion is detected
  320. * obj.description:
  321. * shake:leftright:x-axisfrontback:y-axis:updown:z-axis
  322. *
  323. * @event 'shakefrontback'
  324. * Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly front-back movement.
  325. * obj.description:
  326. * shakefrontback:shakefrontback:y-axis
  327. *
  328. * @event 'shakeleftright'
  329. * Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly left-right movement.
  330. * Additional major movements are mentioned in the obj.description.
  331. * obj.description:
  332. * shakeleftright:leftright:x-axis
  333. *
  334. * @event 'shakeupdown'
  335. * Is triggered when a shaking motion is detected and the gesture can be interpreted as a mainly up-down movement.
  336. * Additional major movements are mentioned in the obj.description.
  337. * obj.description:
  338. * shake:shakeupdown:updown:z-axis
  339. *
  340. * @example
  341. * .bind( eventType, [ eventData ], handler(eventObject) )
  342. * jQuery('body').bind('tapone',function(){alert(arguments[1].description);})
  343. *
  344. */
  345. (function($) {
  346. /**
  347. * General thresholds.
  348. */
  349. // @TODO: move to $...defaults
  350. // @TODO: shake to defaults freeze etc
  351. // change of x deg in y ms
  352. $.jGestures = {};
  353. $.jGestures.defaults = {};
  354. $.jGestures.defaults.thresholdShake = {
  355. requiredShakes : 10,
  356. freezeShakes: 100,
  357. frontback : {
  358. sensitivity: 10
  359. },
  360. leftright : {
  361. sensitivity: 10
  362. },
  363. updown : {
  364. sensitivity: 10
  365. }
  366. };
  367. $.jGestures.defaults.thresholdPinchopen = 0.05;
  368. $.jGestures.defaults.thresholdPinchmove = 0.05;
  369. $.jGestures.defaults.thresholdPinch = 0.05;
  370. $.jGestures.defaults.thresholdPinchclose = 0.05;
  371. $.jGestures.defaults.thresholdRotatecw = 5; //deg
  372. $.jGestures.defaults.thresholdRotateccw = 5; // deg
  373. // a tap becomes a swipe if x/y values changes are above this threshold
  374. $.jGestures.defaults.thresholdMove = 20;
  375. $.jGestures.defaults.thresholdSwipe = 100;
  376. // get capable user agents
  377. $.jGestures.data = {};
  378. $.jGestures.data.capableDevicesInUserAgentString = ['iPad','iPhone','iPod','Mobile Safari']; // basic functionality such as swipe, pinch, rotate, tap should work on every mobile safari, e.g. GalaxyTab
  379. $.jGestures.data.hasGestures = (function () { var _i; for(_i = 0; _i < $.jGestures.data.capableDevicesInUserAgentString.length; _i++ ) { if (navigator.userAgent.indexOf($.jGestures.data.capableDevicesInUserAgentString[_i]) !== -1 ) {return true;} } return false; } )();
  380. $.hasGestures = $.jGestures.data.hasGestures;
  381. $.jGestures.events = {
  382. touchstart : 'jGestures.touchstart',
  383. touchendStart: 'jGestures.touchend;start',
  384. touchendProcessed: 'jGestures.touchend;processed',
  385. gesturestart: 'jGestures.gesturestart',
  386. gestureendStart: 'jGestures.gestureend;start',
  387. gestureendProcessed: 'jGestures.gestureend;processed'
  388. };
  389. jQuery
  390. .each({
  391. // "first domevent necessary"_"touch event+counter" : "exposed as"
  392. // event: orientationchange
  393. orientationchange_orientationchange01: "orientationchange",
  394. // event: gestures
  395. gestureend_pinchopen01: "pinchopen",
  396. gestureend_pinchclose01: "pinchclose",
  397. gestureend_rotatecw01 : 'rotatecw',
  398. gestureend_rotateccw01 : 'rotateccw',
  399. // move events
  400. gesturechange_pinch01: 'pinch',
  401. gesturechange_rotate01: 'rotate',
  402. touchstart_swipe13: 'swipemove',
  403. // event: touches
  404. touchstart_swipe01: "swipeone",
  405. touchstart_swipe02: "swipetwo",
  406. touchstart_swipe03: "swipethree",
  407. touchstart_swipe04: "swipefour",
  408. touchstart_swipe05: 'swipeup',
  409. touchstart_swipe06: 'swiperightup',
  410. touchstart_swipe07: 'swiperight',
  411. touchstart_swipe08: 'swiperightdown',
  412. touchstart_swipe09: 'swipedown',
  413. touchstart_swipe10: 'swipeleftdown',
  414. touchstart_swipe11: 'swipeleft',
  415. touchstart_swipe12: 'swipeleftup',
  416. touchstart_tap01: 'tapone',
  417. touchstart_tap02: 'taptwo',
  418. touchstart_tap03: 'tapthree',
  419. touchstart_tap04: 'tapfour',
  420. devicemotion_shake01: 'shake',
  421. devicemotion_shake02: 'shakefrontback',
  422. devicemotion_shake03: 'shakeleftright',
  423. devicemotion_shake04: 'shakeupdown'
  424. },
  425. /**
  426. * Add gesture events inside the jQuery.event.special namespace
  427. */
  428. function( sInternal_, sPublicFN_ ) {
  429. // add as funciton to jQuery.event.special.sPublicFN_
  430. jQuery.event.special[ sPublicFN_ ] = {
  431. /**
  432. * When the first event handler is bound, jQuery executes the setup function.
  433. * This plugin just uses one eventhandler per element, regardless of the number of bound events.
  434. * All Events are stored internally as properties on the dom-element using the $.data api.
  435. * The setup-function adds the eventlistener, acting as a proxy function for the internal events.
  436. * $.data.ojQueryGestures[_sDOMEvent ('tap') ] = {Boolean}
  437. * @return {Void}
  438. */
  439. setup: function () {
  440. // split the arguments to necessary controll arguements
  441. var _aSplit = sInternal_.split('_');
  442. var _sDOMEvent = _aSplit[0]; //
  443. // get the associated gesture event and strip the counter: necessary for distinguisihng similliar events such as tapone-tapfour
  444. var _sGestureEvent = _aSplit[1].slice(0,_aSplit[1].length-2);
  445. var _$element = jQuery(this);
  446. var _oDatajQueryGestures ;
  447. var oObj;
  448. // bind the event handler on the first $.bind() for a gestureend-event, set marker
  449. if (!_$element.data('ojQueryGestures') || !_$element.data('ojQueryGestures')[_sDOMEvent]) {
  450. // setup pseudo event
  451. _oDatajQueryGestures = _$element.data('ojQueryGestures') || {};
  452. oObj = {};
  453. // marker for: domEvent being set on this element
  454. // e.g.: $.data.oGestureInternals['touchstart'] = true;
  455. // since they're grouped, i'm just marking the first one being added
  456. oObj[_sDOMEvent] = true;
  457. $.extend(true,_oDatajQueryGestures,oObj);
  458. _$element.data('ojQueryGestures' ,_oDatajQueryGestures);
  459. // add gesture events
  460. if($.hasGestures) {
  461. switch(_sGestureEvent) {
  462. // event: orientationchange
  463. case 'orientationchange':
  464. _$element.get(0).addEventListener('orientationchange', _onOrientationchange, false);
  465. break;
  466. // event:
  467. // - shake
  468. // - tilt
  469. case 'shake':
  470. case 'shakefrontback':
  471. case 'shakeleftright':
  472. case 'shakeupdown':
  473. case 'tilt':
  474. //$.hasGyroscope = true //!window.DeviceOrientationEvent;
  475. //_$element.get(0).addEventListener('devicemotion', _onDevicemotion, false);
  476. //_$element.get(0).addEventListener('deviceorientation', _onDeviceorientation, false);
  477. _$element.get(0).addEventListener('devicemotion', _onDevicemotion, false);
  478. break;
  479. // event:
  480. // - touchstart
  481. // - touchmove
  482. // - touchend
  483. case 'tap':
  484. case 'swipe':
  485. case 'swipeup':
  486. case 'swiperightup':
  487. case 'swiperight':
  488. case 'swiperightdown':
  489. case 'swipedown':
  490. case 'swipeleftdown':
  491. case 'swipeleft':
  492. _$element.get(0).addEventListener('touchstart', _onTouchstart, false);
  493. break;
  494. // event: gestureend
  495. case 'pinchopen':
  496. case 'pinchclose' :
  497. case 'rotatecw' :
  498. case 'rotateccw' :
  499. _$element.get(0).addEventListener('gesturestart', _onGesturestart, false);
  500. _$element.get(0).addEventListener('gestureend', _onGestureend, false);
  501. break;
  502. // event: gesturechange
  503. case 'pinch':
  504. case 'rotate':
  505. _$element.get(0).addEventListener('gesturestart', _onGesturestart, false);
  506. _$element.get(0).addEventListener('gesturechange', _onGesturechange, false);
  507. break;
  508. }
  509. }
  510. // create substitute for gesture events
  511. else {
  512. switch(_sGestureEvent) {
  513. // event substitutes:
  514. // - touchstart: mousedown
  515. // - touchmove: none
  516. // - touchend: mouseup
  517. case 'tap':
  518. case 'swipe':
  519. // _$element.get(0).addEventListener('mousedown', _onTouchstart, false);
  520. _$element.bind('mousedown', _onTouchstart);
  521. break;
  522. // no substitution
  523. case 'orientationchange':
  524. case 'pinchopen':
  525. case 'pinchclose' :
  526. case 'rotatecw' :
  527. case 'rotateccw' :
  528. case 'pinch':
  529. case 'rotate':
  530. case 'shake':
  531. case 'tilt':
  532. break;
  533. }
  534. }
  535. }
  536. return false;
  537. },
  538. /**
  539. * For every $.bind(GESTURE) the add-function will be called.
  540. * Instead of binding an actual eventlister, the event is stored as $.data on the element.
  541. * The handler will be triggered using $.triggerHandler(GESTURE) if the internal
  542. * eventhandler (proxy being bound on setup()) detects a GESTURE event
  543. * @param {Object} event_ jQuery-Event-Object being passed by $.bind()
  544. * @return {Void}
  545. */
  546. add : function(event_) {
  547. // add pseudo event: properties on $.data
  548. var _$element = jQuery(this);
  549. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  550. // _oDatajQueryGestures[event_.type] = { 'originalType' : event_.type , 'threshold' : event_.data.threshold, 'preventDefault' : event_.data.preventDefault } ;
  551. _oDatajQueryGestures[event_.type] = { 'originalType' : event_.type } ;
  552. return false;
  553. },
  554. /**
  555. * For every $.unbind(GESTURE) the remove-function will be called.
  556. * Instead of removing the actual eventlister, the event is removed from $.data on the element.
  557. * @param {Object} event_ jQuery-Event-Object being passed by $.bind()
  558. * @return {Void}
  559. */
  560. remove : function(event_) {
  561. // remove pseudo event: properties on $.data
  562. var _$element = jQuery(this);
  563. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  564. _oDatajQueryGestures[event_.type] = false;
  565. _$element.data('ojQueryGestures' ,_oDatajQueryGestures );
  566. return false;
  567. },
  568. /**
  569. * The last $.unbind()-call on the domElement triggers the teardown function
  570. * removing the eventlistener
  571. * @return {Void}
  572. */
  573. // @TODO: maybe rework teardown to work with event type?!
  574. teardown : function() {
  575. // split the arguments to necessary controll arguements
  576. var _aSplit = sInternal_.split('_');
  577. var _sDOMEvent = _aSplit[0]; //
  578. // get the associated gesture event and strip the counter: necessary for distinguisihng similliar events such as tapone-tapfour
  579. var _sGestureEvent = _aSplit[1].slice(0,_aSplit[1].length-2);
  580. var _$element = jQuery(this);
  581. var _oDatajQueryGestures;
  582. var oObj;
  583. // bind the event handler on the first $.bind() for a gestureend-event, set marker
  584. if (!_$element.data('ojQueryGestures') || !_$element.data('ojQueryGestures')[_sDOMEvent]) {
  585. // setup pseudo event
  586. _oDatajQueryGestures = _$element.data('ojQueryGestures') || {};
  587. oObj = {};
  588. // remove marker for: domEvent being set on this element
  589. oObj[_sDOMEvent] = false;
  590. $.extend(true,_oDatajQueryGestures,oObj);
  591. _$element.data('ojQueryGestures' ,_oDatajQueryGestures);
  592. // remove gesture events
  593. if($.hasGestures) {
  594. switch(_sGestureEvent) {
  595. // event: orientationchange
  596. case 'orientationchange':
  597. _$element.get(0).removeEventListener('orientationchange', _onOrientationchange, false);
  598. break;
  599. case 'shake':
  600. case 'shakefrontback':
  601. case 'shakeleftright':
  602. case 'shakeupdown':
  603. case 'tilt':
  604. _$element.get(0).removeEventListener('devicemotion', _onDevicemotion, false);
  605. break;
  606. // event :
  607. // - touchstart
  608. // - touchmove
  609. // - touchend
  610. case 'tap':
  611. case 'swipe':
  612. case 'swipeup':
  613. case 'swiperightup':
  614. case 'swiperight':
  615. case 'swiperightdown':
  616. case 'swipedown':
  617. case 'swipeleftdown':
  618. case 'swipeleft':
  619. case 'swipeleftup':
  620. _$element.get(0).removeEventListener('touchstart', _onTouchstart, false);
  621. _$element.get(0).removeEventListener('touchmove', _onTouchmove, false);
  622. _$element.get(0).removeEventListener('touchend', _onTouchend, false);
  623. break;
  624. // event: gestureend
  625. case 'pinchopen':
  626. case 'pinchclose' :
  627. case 'rotatecw' :
  628. case 'rotateccw' :
  629. _$element.get(0).removeEventListener('gesturestart', _onGesturestart, false);
  630. _$element.get(0).removeEventListener('gestureend', _onGestureend, false);
  631. break;
  632. // event: gesturechange
  633. case 'pinch':
  634. case 'rotate':
  635. _$element.get(0).removeEventListener('gesturestart', _onGesturestart, false);
  636. _$element.get(0).removeEventListener('gesturechange', _onGesturechange, false);
  637. break;
  638. }
  639. }
  640. // remove substitute for gesture events
  641. else {
  642. switch(_sGestureEvent) {
  643. // event substitutes:
  644. // - touchstart: mousedown
  645. // - touchmove: none
  646. // - touchend: mouseup
  647. case 'tap':
  648. case 'swipe':
  649. // _$element.get(0).removeEventListener('mousedown', _onTouchstart, false);
  650. // _$element.get(0).removeEventListener('mousemove', _onTouchmove, false);
  651. // _$element.get(0).removeEventListener('mouseup', _onTouchend, false);
  652. _$element.unbind('mousedown', _onTouchstart);
  653. _$element.unbind('mousemove', _onTouchmove);
  654. _$element.unbind('mouseup', _onTouchend);
  655. break;
  656. // no substitution
  657. case 'orientationchange':
  658. case 'pinchopen':
  659. case 'pinchclose' :
  660. case 'rotatecw' :
  661. case 'rotateccw' :
  662. case 'pinch':
  663. case 'rotate':
  664. case 'shake':
  665. case 'tilt':
  666. break;
  667. }
  668. }
  669. }
  670. return false;
  671. }
  672. };
  673. });
  674. /**
  675. * Creates the object that ist passed as second argument to the $element.triggerHandler function.
  676. * This object contains detailed informations about the gesture event.
  677. * @param {Object} oOptions_ {type: {String}, touches: {String}, deltaY: {String},deltaX : {String}, startMove: {Object}, event:{DOM-Event}, timestamp:{String},vector: {Number}}
  678. * @example _createOptions (
  679. * {
  680. * type: 'swipemove',
  681. * touches: '1',
  682. * deltaY: _iDeltaY,
  683. * deltaX : _iDeltaX,
  684. * startMove: _oDatajQueryGestures.oStartTouch,
  685. * event:event_,
  686. * timestamp:_oEventData.timestamp,
  687. * vector: -1
  688. * }
  689. * );
  690. * @returns {Object}
  691. * {
  692. * type: eventtype e.g. "swipe","pinch",
  693. * originalEvent: {DOM-Event},
  694. * // default: just one entry on the delta-array - the first touchpoint
  695. * // the first touchpoint is the reference point for every gesture,
  696. * // because moving touchpoints in various directions would result in
  697. * // a gesture.
  698. * // delta and direction details are just provided for touch not for gesture / motion events
  699. * delta : [
  700. * {
  701. * lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
  702. * lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
  703. * moved: {Number}, // distance: relative to the original touchpoint
  704. * startX: {Number} , // relative to the original touchpoint
  705. * startY: {Number} ,// relative to the original touchpoint
  706. * } ],
  707. * // based on the first touchpoint
  708. * direction : { // relative to the last touchevent (e.g. touchmove!)
  709. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  710. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  711. * lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  712. * lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  713. * startX: {Number} , //-1,0,+1 relative to the original touchpoint
  714. * startY: {Number} ,// -1,0,+1 relative to the original touchpoint
  715. * },
  716. * rotation: {Number} || null, // gestureonly: amount of rotation relative to the current position NOT the original
  717. * scale: {Number} || null, // gestureonly: amount of scaling relative to the current position NOT the original
  718. * duration: {Number}, // ms: relative to the original touchpoint
  719. * description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
  720. * };
  721. */
  722. function _createOptions(oOptions_) {
  723. // force properties
  724. oOptions_.startMove = (oOptions_.startMove) ? oOptions_.startMove : {startX: null,startY:null,timestamp:null} ;
  725. var _iNow = new Date().getTime();
  726. var _oDirection;
  727. var _oDelta;
  728. // calculate touch differences
  729. if (oOptions_.touches) {
  730. // store delta values
  731. _oDelta = [
  732. {
  733. lastX: oOptions_.deltaX ,
  734. lastY: oOptions_.deltaY,
  735. moved: null,
  736. startX: oOptions_.screenX - oOptions_.startMove.screenX ,
  737. startY: oOptions_.screenY - oOptions_.startMove.screenY
  738. }
  739. ];
  740. _oDirection = {
  741. vector: oOptions_.vector || null,
  742. orientation : window.orientation || null,
  743. lastX : ((_oDelta[0].lastX > 0) ? +1 : ( (_oDelta[0].lastX < 0) ? -1 : 0 ) ),
  744. lastY : ((_oDelta[0].lastY > 0) ? +1 : ( (_oDelta[0].lastY < 0) ? -1 : 0 ) ),
  745. startX : ((_oDelta[0].startX > 0) ? +1 : ( (_oDelta[0].startX < 0) ? -1 : 0 ) ),
  746. startY : ((_oDelta[0].startY > 0) ? +1 : ( (_oDelta[0].startY < 0) ? -1 : 0 ) )
  747. };
  748. // calculate distance traveled using the pythagorean theorem
  749. _oDelta[0].moved = Math.sqrt(Math.pow(Math.abs(_oDelta[0].startX), 2) + Math.pow(Math.abs(_oDelta[0].startY), 2));
  750. }
  751. return {
  752. type: oOptions_.type || null,
  753. originalEvent: oOptions_.event || null,
  754. delta : _oDelta || null,
  755. direction : _oDirection || { orientation : window.orientation || null, vector: oOptions_.vector || null},
  756. duration: (oOptions_.duration) ? oOptions_.duration : ( oOptions_.startMove.timestamp ) ? _iNow - oOptions_.timestamp : null,
  757. rotation: oOptions_.rotation || null,
  758. scale: oOptions_.scale || null,
  759. description : oOptions_.description || [
  760. oOptions_.type,
  761. ':',
  762. oOptions_.touches,
  763. ':',
  764. ((_oDelta[0].lastX != 0) ? ((_oDelta[0].lastX > 0) ? 'right' : 'left') : 'steady'),
  765. ':',
  766. ((_oDelta[0].lastY != 0) ? ( (_oDelta[0].lastY > 0) ? 'down' : 'up') :'steady')
  767. ].join('')
  768. };
  769. }
  770. /**
  771. * DOM-event handlers
  772. */
  773. /**
  774. * Handler: orientationchange
  775. * Triggers the bound orientationchange handler on the window element
  776. * The "orientationchange" handler will receive an object with additional information
  777. * about the event.
  778. * {
  779. * direction : {
  780. * orientation: {-90|0|90|180}
  781. * },
  782. * description : [
  783. * 'orientationchange:{landscape:clockwise:|portrait:default|landscape:counterclockwise|portrait:upsidedown}:{-90|0|90|180}' // e.g. 'orientation:landscape:clockwise:-90
  784. * }
  785. * @param {DOM-Event} event_
  786. * @return {Void}
  787. */
  788. function _onOrientationchange(event_) {
  789. // window.orientation: -90,0,90,180
  790. var _aDict = ['landscape:clockwise:','portrait:default:','landscape:counterclockwise:','portrait:upsidedown:'];
  791. $(window).triggerHandler('orientationchange',
  792. {
  793. direction : {orientation: window.orientation},
  794. description : [
  795. 'orientationchange:',
  796. _aDict[( (window.orientation / 90) +1)],
  797. window.orientation
  798. ].join('')
  799. });
  800. }
  801. /**
  802. * Handler: devicemotion
  803. * Calculates "motion events" such as shake, tilt, wiggle by observing "changes in space"
  804. * For understanding "directions", place your mobile device on a table with the bottom
  805. * (home button) close to you:
  806. * - x-axis: horizontal left / right
  807. * - y-axis: horizontal front / back (through the home button)
  808. * - z-axis: vertical through your device
  809. * @param {DOM-Event} event_
  810. * @returns {Object}
  811. * {
  812. * type: eventtype e.g. "shake",
  813. * originalEvent: {DOM-Event},
  814. * // delta and direction details are just provided for touch not for gesture / motion events
  815. * delta : null,
  816. * direction :{
  817. * vector: null,
  818. * orientation: -90,0,90,180 || null (window.orienntation)
  819. * }
  820. * rotation: {Number} , // amount of rotation relative to the current position NOT the original
  821. * scale: {Number} , // amount of scaling relative to the current position NOT the original
  822. * duration: {Number}, // ms: duration of the motion
  823. * description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
  824. * };
  825. * @param {DOM-Event} event_
  826. * @return {Void}
  827. */
  828. function _onDevicemotion(event_) {
  829. var _sType;
  830. var _$element = jQuery(window);
  831. //var _bHasGyroscope = $.hasGyroscope;
  832. // skip custom notification: devicemotion is triggered every 0.05s regardlesse of any gesture
  833. // get options
  834. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  835. var _oThreshold = $.jGestures.defaults.thresholdShake;
  836. // get last position or set initital values
  837. var _oLastDevicePosition = _oDatajQueryGestures.oDeviceMotionLastDevicePosition || {
  838. accelerationIncludingGravity : {
  839. x: 0,
  840. y: 0,
  841. z: 0
  842. },
  843. shake : {
  844. eventCount: 0,
  845. intervalsPassed: 0,
  846. intervalsFreeze: 0
  847. },
  848. shakeleftright : {
  849. eventCount: 0,
  850. intervalsPassed: 0,
  851. intervalsFreeze: 0
  852. },
  853. shakefrontback : {
  854. eventCount: 0,
  855. intervalsPassed: 0,
  856. intervalsFreeze: 0
  857. },
  858. shakeupdown : {
  859. eventCount: 0,
  860. intervalsPassed: 0,
  861. intervalsFreeze: 0
  862. }
  863. };
  864. // cache current values
  865. var _oCurrentDevicePosition = {
  866. accelerationIncludingGravity : {
  867. x: event_.accelerationIncludingGravity.x,
  868. y: event_.accelerationIncludingGravity.y,
  869. z: event_.accelerationIncludingGravity.z
  870. },
  871. shake: {
  872. eventCount: _oLastDevicePosition.shake.eventCount,
  873. intervalsPassed: _oLastDevicePosition.shake.intervalsPassed,
  874. intervalsFreeze: _oLastDevicePosition.shake.intervalsFreeze
  875. },
  876. shakeleftright: {
  877. eventCount: _oLastDevicePosition.shakeleftright.eventCount,
  878. intervalsPassed: _oLastDevicePosition.shakeleftright.intervalsPassed,
  879. intervalsFreeze: _oLastDevicePosition.shakeleftright.intervalsFreeze
  880. },
  881. shakefrontback: {
  882. eventCount: _oLastDevicePosition.shakefrontback.eventCount,
  883. intervalsPassed: _oLastDevicePosition.shakefrontback.intervalsPassed,
  884. intervalsFreeze: _oLastDevicePosition.shakefrontback.intervalsFreeze
  885. },
  886. shakeupdown: {
  887. eventCount: _oLastDevicePosition.shakeupdown.eventCount,
  888. intervalsPassed: _oLastDevicePosition.shakeupdown.intervalsPassed,
  889. intervalsFreeze: _oLastDevicePosition.shakeupdown.intervalsFreeze
  890. }
  891. };
  892. // options
  893. var _aType;
  894. var _aDescription;
  895. var _oObj;
  896. // trigger events for all bound pseudo events on this element
  897. for (_sType in _oDatajQueryGestures) {
  898. // get current pseudo event
  899. // trigger bound events on this element
  900. switch(_sType) {
  901. case 'shake':
  902. case 'shakeleftright':
  903. case 'shakefrontback':
  904. case 'shakeupdown':
  905. // options
  906. _aType = [];
  907. _aDescription = [];
  908. _aType.push(_sType);
  909. // freeze shake - prevent multiple shake events on one shaking motion (user won't stop shaking immediately)
  910. if (++_oCurrentDevicePosition[_sType].intervalsFreeze > _oThreshold.freezeShakes && _oCurrentDevicePosition[_sType].intervalsFreeze < (2*_oThreshold.freezeShakes) ) { break; }
  911. // set control values
  912. _oCurrentDevicePosition[_sType].intervalsFreeze = 0;
  913. _oCurrentDevicePosition[_sType].intervalsPassed++;
  914. // check for shaking motions: massive acceleration changes in every direction
  915. if ( ( _sType === 'shake' ||_sType === 'shakeleftright' ) && ( _oCurrentDevicePosition.accelerationIncludingGravity.x > _oThreshold.leftright.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.x < (-1* _oThreshold.leftright.sensitivity) ) ) {
  916. _aType.push('leftright');
  917. _aType.push('x-axis');
  918. }
  919. if ( ( _sType === 'shake' ||_sType === 'shakefrontback' ) && (_oCurrentDevicePosition.accelerationIncludingGravity.y > _oThreshold.frontback.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.y < (-1 * _oThreshold.frontback.sensitivity) ) ) {
  920. _aType.push('frontback');
  921. _aType.push('y-axis');
  922. }
  923. if ( ( _sType === 'shake' ||_sType === 'shakeupdown' ) && ( _oCurrentDevicePosition.accelerationIncludingGravity.z+9.81 > _oThreshold.updown.sensitivity || _oCurrentDevicePosition.accelerationIncludingGravity.z+9.81 < (-1 * _oThreshold.updown.sensitivity) ) ) {
  924. _aType.push('updown');
  925. _aType.push('z-axis');
  926. }
  927. // at least one successful shaking event
  928. if (_aType.length > 1) {
  929. // minimum number of shaking motions during the defined "time" (messured by events - device event interval: 0.05s)
  930. if (++_oCurrentDevicePosition[_sType].eventCount == _oThreshold.requiredShakes && (_oCurrentDevicePosition[_sType].intervalsPassed) < _oThreshold.freezeShakes ) {
  931. // send event
  932. _$element.triggerHandler(_sType, _createOptions ({type: _sType, description: _aType.join(':'), event:event_,duration:_oCurrentDevicePosition[_sType].intervalsPassed*5 }) );
  933. // reset
  934. _oCurrentDevicePosition[_sType].eventCount = 0;
  935. _oCurrentDevicePosition[_sType].intervalsPassed = 0;
  936. // freeze shake
  937. _oCurrentDevicePosition[_sType].intervalsFreeze = _oThreshold.freezeShakes+1;
  938. }
  939. // too slow, reset
  940. else if (_oCurrentDevicePosition[_sType].eventCount == _oThreshold.requiredShakes && (_oCurrentDevicePosition[_sType].intervalsPassed) > _oThreshold.freezeShakes ) {
  941. _oCurrentDevicePosition[_sType].eventCount = 0 ;
  942. _oCurrentDevicePosition[_sType].intervalsPassed = 0;
  943. }
  944. }
  945. break;
  946. }
  947. // refresh pseudo events
  948. _oObj = {};
  949. _oObj.oDeviceMotionLastDevicePosition = _oCurrentDevicePosition;
  950. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  951. }
  952. }
  953. /**
  954. * Handler: touchstart or mousedown
  955. * Setup pseudo-event by storing initial values such as :
  956. * screenX : {Number}
  957. * screenY : {Number}
  958. * timestamp: {Number}
  959. * on the pseudo gesture event and
  960. * sets up additional eventlisteners for handling touchmove events.
  961. * @param {DOM-Event} event_
  962. * @return {Void}
  963. */
  964. function _onTouchstart(event_) {
  965. // ignore bubbled handlers
  966. // if ( event_.currentTarget !== event_.target ) { return; }
  967. var _$element = jQuery(event_.currentTarget);
  968. // var _$element = jQuery(event_.target);
  969. // trigger custom notification
  970. _$element.triggerHandler($.jGestures.events.touchstart,event_);
  971. // set the necessary touch events
  972. if($.hasGestures) {
  973. event_.currentTarget.addEventListener('touchmove', _onTouchmove, false);
  974. event_.currentTarget.addEventListener('touchend', _onTouchend, false);
  975. }
  976. // event substitution
  977. else {
  978. // event_.currentTarget.addEventListener('mousemove', _onTouchmove, false);
  979. // event_.currentTarget.addEventListener('mouseup', _onTouchend, false);
  980. _$element.bind('mousemove', _onTouchmove);
  981. _$element.bind('mouseup', _onTouchend);
  982. }
  983. // get stored pseudo event
  984. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  985. // var _oEventData = _oDatajQueryGestures[_sType];
  986. var _eventBase = (event_.touches) ? event_.touches[0] : event_;
  987. // store current values for calculating relative values (changes between touchmoveevents)
  988. var _oObj = {};
  989. _oObj.oLastSwipemove = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
  990. _oObj.oStartTouch = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
  991. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  992. }
  993. /**
  994. * Handler: touchmove or mousemove
  995. * Calculates the x/y changes since the last event,
  996. * compares it to $.jGestures.defaults.thresholdMove and triggers
  997. * an swipemove event if the distance exceed the
  998. * threshold.
  999. * Custom-event argument object:
  1000. * {Object}
  1001. * {
  1002. * type: e.g. 'swipemove',
  1003. * ¡Ö: {DOM-Event},
  1004. * // default: just one entry on the delta-array - the first touchpoint
  1005. * // the first touchpoint is the reference point for every gesture,
  1006. * // because moving touchpoints in various directions would result in
  1007. * // a gesture.
  1008. * // delta and direction details are just provided for touch not for gesture / motion events
  1009. * delta : [
  1010. * {
  1011. * lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
  1012. * lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
  1013. * moved: {Number}, // distance: relative to the original touchpoint
  1014. * startX: {Number} , // relative to the original touchpoint
  1015. * startY: {Number} ,// relative to the original touchpoint
  1016. * } ],
  1017. * // based on the first touchpoint
  1018. * direction : { // relative to the last touchevent (e.g. touchmove!)
  1019. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  1020. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  1021. * lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  1022. * lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  1023. * startX: {Number} , //-1,0,+1 relative to the original touchpoint
  1024. * startY: {Number} ,// -1,0,+1 relative to the original touchpoint
  1025. * },
  1026. * rotation: null, // gestureonly: amount of rotation relative to the current position NOT the original
  1027. * scale: null, // gestureonly: amount of scaling relative to the current position NOT the original
  1028. * duration: {Number}, // ms: relative to the original touchpoint
  1029. * description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
  1030. * };
  1031. *
  1032. * @param {DOM-Event} event_
  1033. * @return {Void}
  1034. */
  1035. function _onTouchmove(event_) {
  1036. var _$element = jQuery(event_.currentTarget);
  1037. // var _$element = jQuery(event_.target);
  1038. // get stored pseudo event
  1039. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  1040. var _bHasTouches = !!event_.touches;
  1041. var _iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
  1042. var _iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
  1043. //relative to the last event
  1044. var _oEventData = _oDatajQueryGestures.oLastSwipemove;
  1045. var _iDeltaX = _iScreenX - _oEventData.screenX ;
  1046. var _iDeltaY = _iScreenY - _oEventData.screenY;
  1047. var _oDetails;
  1048. // there's a swipemove set (not the first occurance), trigger event
  1049. if (!!_oDatajQueryGestures.oLastSwipemove) {
  1050. // check
  1051. _oDetails = _createOptions({type: 'swipemove', touches: (_bHasTouches) ? event_.touches.length: '1', screenY: _iScreenY,screenX:_iScreenX ,deltaY: _iDeltaY,deltaX : _iDeltaX, startMove:_oEventData, event:event_, timestamp:_oEventData.timestamp});
  1052. _$element.triggerHandler(_oDetails.type,_oDetails);
  1053. }
  1054. // store the new values
  1055. var _oObj = {};
  1056. var _eventBase = (event_.touches) ? event_.touches[0] : event_;
  1057. _oObj.oLastSwipemove = { screenX : _eventBase.screenX, screenY : _eventBase.screenY, timestamp:new Date().getTime()};
  1058. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  1059. }
  1060. /**
  1061. * Handler: touchend or mouseup
  1062. * Removes the additional handlers (move/end)
  1063. * Calculates the x/y changes since the touchstart event
  1064. * not in relation to the last move event.
  1065. * Triggers the
  1066. * swipeone|swipetwo|swipethree|swipefour|
  1067. * swipeup|swiperightup|swiperight|swiperightdown|swipedown|
  1068. * swipeleftdown|swipeleft|swipeleftup|
  1069. * tapone|taptwo|tapthree|tapfour
  1070. * event.
  1071. * {Object}
  1072. * {
  1073. * type: eventtype e.g. "swipeone","swipeleftdown",
  1074. * originalEvent: {DOM-Event},
  1075. * // default: just one entry on the delta-array - the first touchpoint
  1076. * // the first touchpoint is the reference point for every gesture,
  1077. * // because moving touchpoints in various directions would result in
  1078. * // a gesture.
  1079. * // delta and direction details are just provided for touch not for gesture / motion events
  1080. * delta : [
  1081. * {
  1082. * lastX:{Number} , // x-axis: relative to the last touchevent (e.g. touchmove!)
  1083. * lastY:{Number}, // y-axis: relative to the last touchevent (e.g. touchmove!)
  1084. * moved: {Number}, // distance: relative to the original touchpoint
  1085. * startX: {Number} , // relative to the original touchpoint
  1086. * startY: {Number} ,// relative to the original touchpoint
  1087. * } ],
  1088. * // based on the first touchpoint
  1089. * direction : { // relative to the last touchevent (e.g. touchmove!)
  1090. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  1091. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  1092. * lastX : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  1093. * lastY : {Number}, // -1,0,+1 relative to the last touchevent (e.g. touchmove!)
  1094. * startX: {Number} , //-1,0,+1 relative to the original touchpoint
  1095. * startY: {Number} ,// -1,0,+1 relative to the original touchpoint
  1096. * },
  1097. * rotation: null,
  1098. * scale: null ,
  1099. * duration: {Number}, // ms: relative to the original touchpoint
  1100. * description : {String} // details as String: {TYPE *}:{TOUCHES 1|2|3|4}:{X-AXIS 'right'|'left'|'steady'}:{Y-AXIS 'down'|'up'|'steady'} e.g. "swipe:1:left:steady" relative to the last touchpoint
  1101. * };
  1102. * @param {DOM-Event} event_
  1103. * @return {Void}
  1104. */
  1105. function _onTouchend(event_) {
  1106. // ignore bubbled handlers
  1107. // if ( event_.currentTarget !== event_.target ) { return; }
  1108. var _$element = jQuery(event_.currentTarget);
  1109. var _bHasTouches = !!event_.changedTouches;
  1110. var _iTouches = (_bHasTouches) ? event_.changedTouches.length : '1';
  1111. var _iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
  1112. var _iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
  1113. // trigger custom notification
  1114. _$element.triggerHandler($.jGestures.events.touchendStart,event_);
  1115. // var _$element = jQuery(event_.target);
  1116. // remove events
  1117. if($.hasGestures) {
  1118. event_.currentTarget.removeEventListener('touchmove', _onTouchmove, false);
  1119. event_.currentTarget.removeEventListener('touchend', _onTouchend, false);
  1120. }
  1121. // event substitution
  1122. else {
  1123. // event_.currentTarget.removeEventListener('mousemove', _onTouchmove, false);
  1124. // event_.currentTarget.removeEventListener('mouseup', _onTouchend, false);
  1125. _$element.unbind('mousemove', _onTouchmove);
  1126. _$element.unbind('mouseup', _onTouchend);
  1127. }
  1128. // get all bound pseudo events
  1129. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  1130. // if the current change on the x/y position is above the defined threshold for moving an element set the moved flag
  1131. // to distinguish between a moving gesture and a shaking finger trying to tap
  1132. var _bHasMoved = (
  1133. Math.abs(_oDatajQueryGestures.oStartTouch.screenX - _iScreenX) > $.jGestures.defaults.thresholdMove ||
  1134. Math.abs(_oDatajQueryGestures.oStartTouch.screenY - _iScreenY) > $.jGestures.defaults.thresholdMove
  1135. ) ? true : false;
  1136. // if the current change on the x/y position is above the defined threshold for swiping set the moved flag
  1137. // to indicate we're dealing with a swipe gesture
  1138. var _bHasSwipeGesture = (
  1139. Math.abs(_oDatajQueryGestures.oStartTouch.screenX - _iScreenX) > $.jGestures.defaults.thresholdSwipe ||
  1140. Math.abs(_oDatajQueryGestures.oStartTouch.screenY - _iScreenY) > $.jGestures.defaults.thresholdSwipe
  1141. ) ? true : false;
  1142. var _sType;
  1143. var _oEventData ;
  1144. var _oDelta;
  1145. // calculate distances in relation to the touchstart position not the last touchmove event!
  1146. var _iDeltaX;
  1147. var _iDeltaY;
  1148. var _oDetails;
  1149. var _aDict = ['zero','one','two','three','four'];
  1150. // swipe marker
  1151. var _bIsSwipe;
  1152. // trigger events for all bound pseudo events on this element
  1153. for (_sType in _oDatajQueryGestures) {
  1154. // get current pseudo event
  1155. _oEventData = _oDatajQueryGestures.oStartTouch;
  1156. _oDelta = {};
  1157. _iScreenX = (_bHasTouches) ? event_.changedTouches[0].screenX : event_.screenX;
  1158. _iScreenY = (_bHasTouches) ? event_.changedTouches[0].screenY : event_.screenY;
  1159. // calculate distances in relation to the touchstart position not the last touchmove event!
  1160. _iDeltaX = _iScreenX - _oEventData.screenX ;
  1161. _iDeltaY = _iScreenY - _oEventData.screenY;
  1162. _oDetails = _createOptions({type: 'swipe', touches: _iTouches, screenY: _iScreenY,screenX:_iScreenX ,deltaY: _iDeltaY,deltaX : _iDeltaX, startMove:_oEventData, event:event_, timestamp: _oEventData.timestamp });
  1163. // swipe marker
  1164. _bIsSwipe = false;
  1165. // trigger bound events on this element
  1166. switch(_sType) {
  1167. case 'swipeone':
  1168. if( _bHasTouches === false && _iTouches == 1 && _bHasMoved === false){
  1169. // trigger tapone!
  1170. break;
  1171. }
  1172. if (_bHasTouches===false || ( _iTouches == 1 && _bHasMoved === true && _bHasSwipeGesture===true)) {
  1173. _bIsSwipe = true;
  1174. _oDetails.type = ['swipe',_aDict[_iTouches]].join('');
  1175. _$element.triggerHandler(_oDetails.type,_oDetails);
  1176. }
  1177. break;
  1178. case 'swipetwo':
  1179. if (( _bHasTouches && _iTouches== 2 && _bHasMoved === true && _bHasSwipeGesture===true)) {
  1180. _bIsSwipe = true;
  1181. _oDetails.type = ['swipe',_aDict[_iTouches]].join('');
  1182. _$element.triggerHandler(_oDetails.type,_oDetails);
  1183. }
  1184. break;
  1185. case 'swipethree':
  1186. if ( ( _bHasTouches && _iTouches == 3 && _bHasMoved === true && _bHasSwipeGesture===true)) {
  1187. _bIsSwipe = true;
  1188. _oDetails.type = ['swipe',_aDict[_iTouches]].join('');
  1189. _$element.triggerHandler(_oDetails.type,_oDetails);
  1190. }
  1191. break;
  1192. case 'swipefour':
  1193. if ( ( _bHasTouches && _iTouches == 4 && _bHasMoved === true && _bHasSwipeGesture===true)) {
  1194. _bIsSwipe = true;
  1195. _oDetails.type = ['swipe',_aDict[_iTouches]].join('');
  1196. _$element.triggerHandler(_oDetails.type,_oDetails);
  1197. }
  1198. break;
  1199. case 'swipeup':
  1200. case 'swiperightup':
  1201. case 'swiperight':
  1202. case 'swiperightdown':
  1203. case 'swipedown':
  1204. case 'swipeleftdown':
  1205. case 'swipeleft':
  1206. case 'swipeleftup':
  1207. if ( _bHasTouches && _bHasMoved === true && _bHasSwipeGesture===true) {
  1208. _bIsSwipe = true;
  1209. _oDetails.type = [
  1210. 'swipe',
  1211. ((_oDetails.delta[0].lastX != 0) ? ((_oDetails.delta[0].lastX > 0) ? 'right' : 'left') : ''),
  1212. ((_oDetails.delta[0].lastY != 0) ? ((_oDetails.delta[0].lastY > 0) ? 'down' : 'up') :'')
  1213. ].join('');
  1214. _$element.triggerHandler(_oDetails.type, _oDetails);
  1215. }
  1216. break;
  1217. case 'tapone':
  1218. case 'taptwo':
  1219. case 'tapthree':
  1220. case 'tapfour':
  1221. if (( /* _bHasTouches && */ _bHasMoved !== true && _bIsSwipe !==true) && (_aDict[_iTouches] ==_sType.slice(3)) ) {
  1222. _oDetails.description = ['tap',_aDict[_iTouches]].join('');
  1223. _oDetails.type = ['tap',_aDict[_iTouches]].join('');
  1224. _$element.triggerHandler(_oDetails.type,_oDetails);
  1225. }
  1226. break;
  1227. }
  1228. // refresh pseudo events
  1229. var _oObj = {};
  1230. // _oObj[_sType] = false;
  1231. // _oObj.hasTouchmoved = false;
  1232. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  1233. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  1234. }
  1235. _$element.triggerHandler($.jGestures.events.touchendProcessed,event_);
  1236. }
  1237. /**
  1238. * Handler: gesturestart
  1239. * Setup pseudo-event by storing initial values such as :
  1240. * timestamp: {Number}
  1241. * on the pseudo gesture event
  1242. * Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
  1243. * @param {DOM-Event} event_
  1244. * @return {Void}
  1245. */
  1246. function _onGesturestart(event_) {
  1247. // ignore bubbled handlers
  1248. // if ( event_.currentTarget !== event_.target ) { return; }
  1249. var _$element = jQuery(event_.currentTarget);
  1250. // var _$element = jQuery(event_.target);
  1251. // trigger custom notification
  1252. _$element.triggerHandler($.jGestures.events.gesturestart,event_);
  1253. // get stored pseudo event
  1254. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  1255. // var _oEventData = _oDatajQueryGestures[_sType];
  1256. // store current values for calculating relative values (changes between touchmoveevents)
  1257. var _oObj = {};
  1258. _oObj.oStartTouch = {timestamp:new Date().getTime()};
  1259. _$element.data('ojQueryGestures',$.extend(true,_oDatajQueryGestures,_oObj));
  1260. }
  1261. /**
  1262. * Handler: gesturechange
  1263. * Read the event_.scale / event_.rotate values,
  1264. * an triggers a pinch|rotate event if necessary.
  1265. * Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
  1266. * @returns {Object}
  1267. * {
  1268. * type: eventtype e.g. "pinch","rotate",
  1269. * originalEvent: {DOM-Event},
  1270. * // delta and direction details are just provided for touch not for gesture / motion events
  1271. * delta : null,
  1272. * direction : {
  1273. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  1274. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  1275. * },
  1276. * rotation: {Number} , // amount of rotation relative to the current position NOT the original
  1277. * scale: {Number} , // amount of scaling relative to the current position NOT the original
  1278. * duration: {Number}, // ms: relative to the original touchpoint
  1279. * description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
  1280. * };
  1281. * @param {DOM-Event} event_
  1282. * @return {Void}
  1283. */
  1284. function _onGesturechange(event_) {
  1285. // ignore bubbled handlers
  1286. // if ( event_.currentTarget !== event_.target ) { return; }
  1287. var _$element = jQuery(event_.currentTarget);
  1288. // var _$element = jQuery(event_.target);
  1289. var _iDelta,_iDirection,_sDesc,_oDetails;
  1290. // get all pseudo events
  1291. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  1292. // trigger events for all bound pseudo events on this element
  1293. var _sType;
  1294. for (_sType in _oDatajQueryGestures) {
  1295. // trigger a specific bound event
  1296. switch(_sType) {
  1297. case 'pinch':
  1298. _iDelta = event_.scale;
  1299. if ( ( ( _iDelta < 1 ) && (_iDelta % 1) < (1 - $.jGestures.defaults.thresholdPinchclose) ) || ( ( _iDelta > 1 ) && (_iDelta % 1) > ($.jGestures.defaults.thresholdPinchopen) ) ) {
  1300. _iDirection = (_iDelta < 1 ) ? -1 : +1 ;
  1301. _oDetails = _createOptions({type: 'pinch', scale: _iDelta, touches: null,startMove:_oDatajQueryGestures.oStartTouch, event:event_, timestamp: _oDatajQueryGestures.oStartTouch.timestamp, vector:_iDirection, description: ['pinch:',_iDirection,':' , ( (_iDelta < 1 ) ? 'close' : 'open' )].join('') });
  1302. _$element.triggerHandler(_oDetails.type, _oDetails);
  1303. }
  1304. break;
  1305. case 'rotate':
  1306. _iDelta = event_.rotation;
  1307. if ( ( ( _iDelta < 1 ) && ( -1*(_iDelta) > $.jGestures.defaults.thresholdRotateccw ) ) || ( ( _iDelta > 1 ) && (_iDelta > $.jGestures.defaults.thresholdRotatecw) ) ) {
  1308. _iDirection = (_iDelta < 1 ) ? -1 : +1 ;
  1309. _oDetails = _createOptions({type: 'rotate', rotation: _iDelta, touches: null, startMove:_oDatajQueryGestures.oStartTouch, event:event_, timestamp: _oDatajQueryGestures.oStartTouch.timestamp, vector:_iDirection, description: ['rotate:',_iDirection,':' , ( (_iDelta < 1 ) ? 'counterclockwise' : 'clockwise' )].join('') });
  1310. _$element.triggerHandler(_oDetails.type, _oDetails);
  1311. }
  1312. break;
  1313. }
  1314. }
  1315. }
  1316. /**
  1317. * Handler: gestureend
  1318. * Read the event_.scale / event_.rotate values,
  1319. * compares it to $.jGestures.defaults.threshold* and triggers
  1320. * a pinchclose|pinchclose|rotatecw|rotateccw event if the distance exceed the
  1321. * Since the gesture-event doesn't supply event.touches no tuchpoints will be calculated
  1322. * * Custom-event argument object:
  1323. * @returns {Object}
  1324. * {
  1325. * type: eventtype e.g. "pinchclose","pinchopen", "rotatecw", "rotateccw",
  1326. * originalEvent: {DOM-Event},
  1327. * // delta and direction details are just provided for touch not for gesture / motion events
  1328. * delta : null,
  1329. * // based on the first touchpoint
  1330. * direction : {
  1331. * vector: {Number}, // -1|+1, indicates the direction if necessary(pinch/rotate)
  1332. * orientation: {Number} // window.orientation: -90,0,90,180 || null (window.orienntation)
  1333. * },
  1334. * rotation: {Number} , // amount of rotation relative to the current position NOT the original
  1335. * scale: {Number} , // amount of scaling relative to the current position NOT the original
  1336. * duration: {Number}, // ms: relative to the original touchpoint
  1337. * description : {String} // details as String: pinch:{'close'|'open'} e.g. "pinch:-1:close" || rotate:{'counterclockwise'|'clockwise'} e.g. "rotate:-1:counterclockwise"
  1338. * };
  1339. * @param {DOM-Event} event_
  1340. * @return {Void}
  1341. */
  1342. function _onGestureend(event_) {
  1343. // ignore bubbled handlers
  1344. // if ( event_.currentTarget !== event_.target ) { return; }
  1345. var _$element = jQuery(event_.currentTarget);
  1346. // var _$element = jQuery(event_.target);
  1347. // trigger custom notification
  1348. _$element.triggerHandler($.jGestures.events.gestureendStart,event_);
  1349. var _iDelta;
  1350. var _oDatajQueryGestures = _$element.data('ojQueryGestures');
  1351. // trigger handler for every bound event
  1352. var _sType;
  1353. for (_sType in _oDatajQueryGestures) {
  1354. switch(_sType) {
  1355. case 'pinchclose':
  1356. _iDelta = event_.scale;
  1357. if (( _iDelta < 1 ) && (_iDelta % 1) < (1 - $.jGestures.defaults.thresholdPinchclose)) {
  1358. _$element.triggerHandler('pinchclose', _createOptions ({type: 'pinchclose', scale:_iDelta, vector: -1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'pinch:-1:close' }) );
  1359. }
  1360. break;
  1361. case 'pinchopen':
  1362. _iDelta = event_.scale;
  1363. if ( ( _iDelta > 1 ) && (_iDelta % 1) > ($.jGestures.defaults.thresholdPinchopen) ) {
  1364. _$element.triggerHandler('pinchopen', _createOptions ({type: 'pinchopen', scale:_iDelta, vector: +1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'pinch:+1:open'}) );
  1365. }
  1366. break;
  1367. case 'rotatecw':
  1368. _iDelta = event_.rotation;
  1369. if ( ( _iDelta > 1 ) && (_iDelta > $.jGestures.defaults.thresholdRotatecw) ) {
  1370. _$element.triggerHandler('rotatecw', _createOptions ({type: 'rotatecw', rotation:_iDelta, vector: +1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'rotate:+1:clockwise'}) );
  1371. }
  1372. break;
  1373. case 'rotateccw':
  1374. _iDelta = event_.rotation;
  1375. if ( ( _iDelta < 1 ) && ( -1*(_iDelta) > $.jGestures.defaults.thresholdRotateccw ) ) {
  1376. _$element.triggerHandler('rotateccw', _createOptions ({type: 'rotateccw', rotation:_iDelta, vector: -1, touches: null, startMove: _oDatajQueryGestures.oStartTouch, event:event_, timestamp:_oDatajQueryGestures.oStartTouch.timestamp,description: 'rotate:-1:counterclockwise'}) );
  1377. }
  1378. break;
  1379. }
  1380. }
  1381. _$element.triggerHandler($.jGestures.events.gestureendProcessed,event_);
  1382. }
  1383. }
  1384. )(jQuery);