index.js 81 KB

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