index.js 74 KB

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