1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050 |
- /*
- * Gesture detector library that forked from github.com/EightMedia/hammer.js.
- */
-
- 'use strict';
-
- import _Object$keys from 'babel-runtime/core-js/object/keys';
- import util from './util';
-
- var Event, Utils, Detection, PointerEvent;
-
- /**
- * @object ons.GestureDetector
- * @category gesture
- * @description
- * [en]Utility class for gesture detection.[/en]
- * [ja]ジェスチャを検知するためのユーティリティクラスです。[/ja]
- */
-
- /**
- * @method constructor
- * @signature constructor(element[, options])
- * @description
- * [en]Create a new GestureDetector instance.[/en]
- * [ja]GestureDetectorのインスタンスを生成します。[/ja]
- * @param {Element} element
- * [en]Name of the event.[/en]
- * [ja]ジェスチャを検知するDOM要素を指定します。[/ja]
- * @param {Object} [options]
- * [en]Options object.[/en]
- * [ja]オプションを指定します。[/ja]
- * @return {ons.GestureDetector.Instance}
- */
- var GestureDetector = function GestureDetector(element, options) {
- return new GestureDetector.Instance(element, options || {});
- };
-
- /**
- * default settings.
- * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled
- * by setting it's name (like `swipe`) to false.
- * You can set the defaults for all instances by changing this object before creating an instance.
- * @example
- * ````
- * GestureDetector.defaults.drag = false;
- * GestureDetector.defaults.behavior.touchAction = 'pan-y';
- * delete GestureDetector.defaults.behavior.userSelect;
- * ````
- * @property defaults
- * @type {Object}
- */
- GestureDetector.defaults = {
- behavior: {
- // userSelect: 'none', // Also disables selection in `input` children
- touchAction: 'pan-y',
- touchCallout: 'none',
- contentZooming: 'none',
- userDrag: 'none',
- tapHighlightColor: 'rgba(0,0,0,0)'
- }
- };
-
- /**
- * GestureDetector document where the base events are added at
- * @property DOCUMENT
- * @type {HTMLElement}
- * @default window.document
- */
- GestureDetector.DOCUMENT = document;
-
- /**
- * detect support for pointer events
- * @property HAS_POINTEREVENTS
- * @type {Boolean}
- */
- GestureDetector.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled;
-
- /**
- * detect support for touch events
- * @property HAS_TOUCHEVENTS
- * @type {Boolean}
- */
- GestureDetector.HAS_TOUCHEVENTS = 'ontouchstart' in window;
-
- /**
- * detect mobile browsers
- * @property IS_MOBILE
- * @type {Boolean}
- */
- GestureDetector.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent);
-
- /**
- * detect if we want to support mouseevents at all
- * @property NO_MOUSEEVENTS
- * @type {Boolean}
- */
- GestureDetector.NO_MOUSEEVENTS = GestureDetector.HAS_TOUCHEVENTS && GestureDetector.IS_MOBILE || GestureDetector.HAS_POINTEREVENTS;
-
- /**
- * interval in which GestureDetector recalculates current velocity/direction/angle in ms
- * @property CALCULATE_INTERVAL
- * @type {Number}
- * @default 25
- */
- GestureDetector.CALCULATE_INTERVAL = 25;
-
- /**
- * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup`
- * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`)
- * @property EVENT_TYPES
- * @private
- * @writeOnce
- * @type {Object}
- */
- var EVENT_TYPES = {};
-
- /**
- * direction strings, for safe comparisons
- * @property DIRECTION_DOWN|LEFT|UP|RIGHT
- * @final
- * @type {String}
- * @default 'down' 'left' 'up' 'right'
- */
- var DIRECTION_DOWN = GestureDetector.DIRECTION_DOWN = 'down';
- var DIRECTION_LEFT = GestureDetector.DIRECTION_LEFT = 'left';
- var DIRECTION_UP = GestureDetector.DIRECTION_UP = 'up';
- var DIRECTION_RIGHT = GestureDetector.DIRECTION_RIGHT = 'right';
-
- /**
- * pointertype strings, for safe comparisons
- * @property POINTER_MOUSE|TOUCH|PEN
- * @final
- * @type {String}
- * @default 'mouse' 'touch' 'pen'
- */
- var POINTER_MOUSE = GestureDetector.POINTER_MOUSE = 'mouse';
- var POINTER_TOUCH = GestureDetector.POINTER_TOUCH = 'touch';
- var POINTER_PEN = GestureDetector.POINTER_PEN = 'pen';
-
- /**
- * eventtypes
- * @property EVENT_START|MOVE|END|RELEASE|TOUCH
- * @final
- * @type {String}
- * @default 'start' 'change' 'move' 'end' 'release' 'touch'
- */
- var EVENT_START = GestureDetector.EVENT_START = 'start';
- var EVENT_MOVE = GestureDetector.EVENT_MOVE = 'move';
- var EVENT_END = GestureDetector.EVENT_END = 'end';
- var EVENT_RELEASE = GestureDetector.EVENT_RELEASE = 'release';
- var EVENT_TOUCH = GestureDetector.EVENT_TOUCH = 'touch';
-
- /**
- * if the window events are set...
- * @property READY
- * @writeOnce
- * @type {Boolean}
- * @default false
- */
- GestureDetector.READY = false;
-
- /**
- * plugins namespace
- * @property plugins
- * @type {Object}
- */
- GestureDetector.plugins = GestureDetector.plugins || {};
-
- /**
- * gestures namespace
- * see `/gestures` for the definitions
- * @property gestures
- * @type {Object}
- */
- GestureDetector.gestures = GestureDetector.gestures || {};
-
- /**
- * setup events to detect gestures on the document
- * this function is called when creating an new instance
- * @private
- */
- function setup(opts) {
- if (GestureDetector.READY) {
- return;
- }
-
- // find what eventtypes we add listeners to
- Event.determineEventTypes();
-
- // Register all gestures inside GestureDetector.gestures
- Utils.each(GestureDetector.gestures, function (gesture) {
- Detection.register(gesture);
- });
-
- // Add touch events on the document
- Event.onTouch(GestureDetector.DOCUMENT, EVENT_MOVE, Detection.detect, opts);
- Event.onTouch(GestureDetector.DOCUMENT, EVENT_END, Detection.detect, opts);
-
- // GestureDetector is ready...!
- GestureDetector.READY = true;
- }
-
- /**
- * @module GestureDetector
- *
- * @class Utils
- * @static
- */
- Utils = GestureDetector.utils = {
- /**
- * extend method, could also be used for cloning when `dest` is an empty object.
- * changes the dest object
- * @param {Object} dest
- * @param {Object} src
- * @param {Boolean} [merge=false] do a merge
- * @return {Object} dest
- */
- extend: function extend(dest, src, merge) {
- for (var key in src) {
- if (src.hasOwnProperty(key) && (dest[key] === undefined || !merge)) {
- dest[key] = src[key];
- }
- }
- return dest;
- },
-
- /**
- * simple addEventListener wrapper
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- */
- on: function on(element, type, handler, opt) {
- util.addEventListener(element, type, handler, opt, true);
- },
-
- /**
- * simple removeEventListener wrapper
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- */
- off: function off(element, type, handler, opt) {
- util.removeEventListener(element, type, handler, opt, true);
- },
-
- /**
- * forEach over arrays and objects
- * @param {Object|Array} obj
- * @param {Function} iterator
- * @param {any} iterator.item
- * @param {Number} iterator.index
- * @param {Object|Array} iterator.obj the source object
- * @param {Object} context value to use as `this` in the iterator
- */
- each: function each(obj, iterator, context) {
- var i, len;
-
- // native forEach on arrays
- if ('forEach' in obj) {
- obj.forEach(iterator, context);
- // arrays
- } else if (obj.length !== undefined) {
- for (i = 0, len = obj.length; i < len; i++) {
- if (iterator.call(context, obj[i], i, obj) === false) {
- return;
- }
- }
- // objects
- } else {
- for (i in obj) {
- if (obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj) === false) {
- return;
- }
- }
- }
- },
-
- /**
- * find if a string contains the string using indexOf
- * @param {String} src
- * @param {String} find
- * @return {Boolean} found
- */
- inStr: function inStr(src, find) {
- return src.indexOf(find) > -1;
- },
-
- /**
- * find if a array contains the object using indexOf or a simple polyfill
- * @param {String} src
- * @param {String} find
- * @return {Boolean|Number} false when not found, or the index
- */
- inArray: function inArray(src, find, deep) {
- if (deep) {
- for (var i = 0, len = src.length; i < len; i++) {
- // Array.findIndex
- if (_Object$keys(find).every(function (key) {
- return src[i][key] === find[key];
- })) {
- return i;
- }
- }
- return -1;
- }
-
- if (src.indexOf) {
- return src.indexOf(find);
- } else {
- for (var i = 0, len = src.length; i < len; i++) {
- if (src[i] === find) {
- return i;
- }
- }
- return -1;
- }
- },
-
- /**
- * convert an array-like object (`arguments`, `touchlist`) to an array
- * @param {Object} obj
- * @return {Array}
- */
- toArray: function toArray(obj) {
- return Array.prototype.slice.call(obj, 0);
- },
-
- /**
- * find if a node is in the given parent
- * @param {HTMLElement} node
- * @param {HTMLElement} parent
- * @return {Boolean} found
- */
- hasParent: function hasParent(node, parent) {
- while (node) {
- if (node == parent) {
- return true;
- }
- node = node.parentNode;
- }
- return false;
- },
-
- /**
- * get the center of all the touches
- * @param {Array} touches
- * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties
- */
- getCenter: function getCenter(touches) {
- var pageX = [],
- pageY = [],
- clientX = [],
- clientY = [],
- min = Math.min,
- max = Math.max;
-
- // no need to loop when only one touch
- if (touches.length === 1) {
- return {
- pageX: touches[0].pageX,
- pageY: touches[0].pageY,
- clientX: touches[0].clientX,
- clientY: touches[0].clientY
- };
- }
-
- Utils.each(touches, function (touch) {
- pageX.push(touch.pageX);
- pageY.push(touch.pageY);
- clientX.push(touch.clientX);
- clientY.push(touch.clientY);
- });
-
- return {
- pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2,
- pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2,
- clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2,
- clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2
- };
- },
-
- /**
- * calculate the velocity between two points. unit is in px per ms.
- * @param {Number} deltaTime
- * @param {Number} deltaX
- * @param {Number} deltaY
- * @return {Object} velocity `x` and `y`
- */
- getVelocity: function getVelocity(deltaTime, deltaX, deltaY) {
- return {
- x: Math.abs(deltaX / deltaTime) || 0,
- y: Math.abs(deltaY / deltaTime) || 0
- };
- },
-
- /**
- * calculate the angle between two coordinates
- * @param {Touch} touch1
- * @param {Touch} touch2
- * @return {Number} angle
- */
- getAngle: function getAngle(touch1, touch2) {
- var x = touch2.clientX - touch1.clientX,
- y = touch2.clientY - touch1.clientY;
-
- return Math.atan2(y, x) * 180 / Math.PI;
- },
-
- /**
- * do a small comparison to get the direction between two touches.
- * @param {Touch} touch1
- * @param {Touch} touch2
- * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN`
- */
- getDirection: function getDirection(touch1, touch2) {
- var x = Math.abs(touch1.clientX - touch2.clientX),
- y = Math.abs(touch1.clientY - touch2.clientY);
-
- if (x >= y) {
- return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
- }
- return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN;
- },
-
- /**
- * calculate the distance between two touches
- * @param {Touch}touch1
- * @param {Touch} touch2
- * @return {Number} distance
- */
- getDistance: function getDistance(touch1, touch2) {
- var x = touch2.clientX - touch1.clientX,
- y = touch2.clientY - touch1.clientY;
-
- return Math.sqrt(x * x + y * y);
- },
-
- /**
- * calculate the scale factor between two touchLists
- * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
- * @param {Array} start array of touches
- * @param {Array} end array of touches
- * @return {Number} scale
- */
- getScale: function getScale(start, end) {
- // need two fingers...
- if (start.length >= 2 && end.length >= 2) {
- return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]);
- }
- return 1;
- },
-
- /**
- * calculate the rotation degrees between two touchLists
- * @param {Array} start array of touches
- * @param {Array} end array of touches
- * @return {Number} rotation
- */
- getRotation: function getRotation(start, end) {
- // need two fingers
- if (start.length >= 2 && end.length >= 2) {
- return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]);
- }
- return 0;
- },
-
- /**
- * find out if the direction is vertical *
- * @param {String} direction matches `DIRECTION_UP|DOWN`
- * @return {Boolean} is_vertical
- */
- isVertical: function isVertical(direction) {
- return direction == DIRECTION_UP || direction == DIRECTION_DOWN;
- },
-
- /**
- * set css properties with their prefixes
- * @param {HTMLElement} element
- * @param {String} prop
- * @param {String} value
- * @param {Boolean} [toggle=true]
- * @return {Boolean}
- */
- setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) {
- var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms'];
- prop = Utils.toCamelCase(prop);
-
- for (var i = 0; i < prefixes.length; i++) {
- var p = prop;
- // prefixes
- if (prefixes[i]) {
- p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1);
- }
-
- // test the style
- if (p in element.style) {
- element.style[p] = (toggle === null || toggle) && value || '';
- break;
- }
- }
- },
-
- /**
- * toggle browser default behavior by setting css properties.
- * `userSelect='none'` also sets `element.onselectstart` to false
- * `userDrag='none'` also sets `element.ondragstart` to false
- *
- * @param {HtmlElement} element
- * @param {Object} props
- * @param {Boolean} [toggle=true]
- */
- toggleBehavior: function toggleBehavior(element, props, toggle) {
- if (!props || !element || !element.style) {
- return;
- }
-
- // set the css properties
- Utils.each(props, function (value, prop) {
- Utils.setPrefixedCss(element, prop, value, toggle);
- });
-
- var falseFn = toggle && function () {
- return false;
- };
-
- // also the disable onselectstart
- if (props.userSelect == 'none') {
- element.onselectstart = falseFn;
- }
- // and disable ondragstart
- if (props.userDrag == 'none') {
- element.ondragstart = falseFn;
- }
- },
-
- /**
- * convert a string with underscores to camelCase
- * so prevent_default becomes preventDefault
- * @param {String} str
- * @return {String} camelCaseStr
- */
- toCamelCase: function toCamelCase(str) {
- return str.replace(/[_-]([a-z])/g, function (s) {
- return s[1].toUpperCase();
- });
- }
- };
-
- /**
- * @module GestureDetector
- */
- /**
- * @class Event
- * @static
- */
- Event = GestureDetector.event = {
- /**
- * when touch events have been fired, this is true
- * this is used to stop mouse events
- * @property prevent_mouseevents
- * @private
- * @type {Boolean}
- */
- preventMouseEvents: false,
-
- /**
- * if EVENT_START has been fired
- * @property started
- * @private
- * @type {Boolean}
- */
- started: false,
-
- /**
- * when the mouse is hold down, this is true
- * @property should_detect
- * @private
- * @type {Boolean}
- */
- shouldDetect: false,
-
- /**
- * simple event binder with a hook and support for multiple types
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- * @param {Object} [opt]
- * @param {Function} [hook]
- * @param {Object} hook.type
- */
- on: function on(element, type, handler, opt, hook) {
- var types = type.split(' ');
- Utils.each(types, function (type) {
- Utils.on(element, type, handler, opt);
- hook && hook(type);
- });
- },
-
- /**
- * simple event unbinder with a hook and support for multiple types
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- * @param {Object} [opt]
- * @param {Function} [hook]
- * @param {Object} hook.type
- */
- off: function off(element, type, handler, opt, hook) {
- var types = type.split(' ');
- Utils.each(types, function (type) {
- Utils.off(element, type, handler, opt);
- hook && hook(type);
- });
- },
-
- /**
- * the core touch event handler.
- * this finds out if we should to detect gestures
- * @param {HTMLElement} element
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Function} handler
- * @return onTouchHandler {Function} the core event handler
- */
- onTouch: function onTouch(element, eventType, handler, opt) {
- var self = this;
-
- var onTouchHandler = function onTouchHandler(ev) {
- var srcType = ev.type.toLowerCase(),
- isPointer = GestureDetector.HAS_POINTEREVENTS,
- isMouse = Utils.inStr(srcType, 'mouse'),
- triggerType;
-
- // if we are in a mouseevent, but there has been a touchevent triggered in this session
- // we want to do nothing. simply break out of the event.
- if (isMouse && self.preventMouseEvents) {
- return;
-
- // mousebutton must be down
- } else if (isMouse && eventType == EVENT_START && ev.button === 0) {
- self.preventMouseEvents = false;
- self.shouldDetect = true;
- } else if (isPointer && eventType == EVENT_START) {
- self.shouldDetect = ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev);
- // just a valid start event, but no mouse
- } else if (!isMouse && eventType == EVENT_START) {
- self.preventMouseEvents = true;
- self.shouldDetect = true;
- }
-
- // update the pointer event before entering the detection
- if (isPointer && eventType != EVENT_END) {
- PointerEvent.updatePointer(eventType, ev);
- }
-
- // we are in a touch/down state, so allowed detection of gestures
- if (self.shouldDetect) {
- triggerType = self.doDetect.call(self, ev, eventType, element, handler);
- }
-
- // ...and we are done with the detection
- // so reset everything to start each detection totally fresh
- if (triggerType == EVENT_END) {
- self.preventMouseEvents = false;
- self.shouldDetect = false;
- PointerEvent.reset();
- // update the pointerevent object after the detection
- }
-
- if (isPointer && eventType == EVENT_END) {
- PointerEvent.updatePointer(eventType, ev);
- }
- };
-
- this.on(element, EVENT_TYPES[eventType], onTouchHandler, opt);
- return onTouchHandler;
- },
-
- /**
- * the core detection method
- * this finds out what GestureDetector-touch-events to trigger
- * @param {Object} ev
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {HTMLElement} element
- * @param {Function} handler
- * @return {String} triggerType matches `EVENT_START|MOVE|END`
- */
- doDetect: function doDetect(ev, eventType, element, handler) {
- var touchList = this.getTouchList(ev, eventType);
- var touchListLength = touchList.length;
- var triggerType = eventType;
- var triggerChange = touchList.trigger; // used by fakeMultitouch plugin
- var changedLength = touchListLength;
-
- // at each touchstart-like event we want also want to trigger a TOUCH event...
- if (eventType == EVENT_START) {
- triggerChange = EVENT_TOUCH;
- // ...the same for a touchend-like event
- } else if (eventType == EVENT_END) {
- triggerChange = EVENT_RELEASE;
-
- // keep track of how many touches have been removed
- changedLength = touchList.length - (ev.changedTouches ? ev.changedTouches.length : 1);
- }
-
- // after there are still touches on the screen,
- // we just want to trigger a MOVE event. so change the START or END to a MOVE
- // but only after detection has been started, the first time we actually want a START
- if (changedLength > 0 && this.started) {
- triggerType = EVENT_MOVE;
- }
-
- // detection has been started, we keep track of this, see above
- this.started = true;
-
- // generate some event data, some basic information
- var evData = this.collectEventData(element, triggerType, touchList, ev);
-
- // trigger the triggerType event before the change (TOUCH, RELEASE) events
- // but the END event should be at last
- if (eventType != EVENT_END) {
- handler.call(Detection, evData);
- }
-
- // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed
- if (triggerChange) {
- evData.changedLength = changedLength;
- evData.eventType = triggerChange;
-
- handler.call(Detection, evData);
-
- evData.eventType = triggerType;
- delete evData.changedLength;
- }
-
- // trigger the END event
- if (triggerType == EVENT_END) {
- handler.call(Detection, evData);
-
- // ...and we are done with the detection
- // so reset everything to start each detection totally fresh
- this.started = false;
- }
-
- return triggerType;
- },
-
- /**
- * we have different events for each device/browser
- * determine what we need and set them in the EVENT_TYPES constant
- * the `onTouch` method is bind to these properties.
- * @return {Object} events
- */
- determineEventTypes: function determineEventTypes() {
- var types;
- if (GestureDetector.HAS_POINTEREVENTS) {
- if (window.PointerEvent) {
- types = ['pointerdown', 'pointermove', 'pointerup pointercancel lostpointercapture'];
- } else {
- types = ['MSPointerDown', 'MSPointerMove', 'MSPointerUp MSPointerCancel MSLostPointerCapture'];
- }
- } else if (GestureDetector.NO_MOUSEEVENTS) {
- types = ['touchstart', 'touchmove', 'touchend touchcancel'];
- } else {
- types = ['touchstart mousedown', 'touchmove mousemove', 'touchend touchcancel mouseup'];
- }
-
- EVENT_TYPES[EVENT_START] = types[0];
- EVENT_TYPES[EVENT_MOVE] = types[1];
- EVENT_TYPES[EVENT_END] = types[2];
- return EVENT_TYPES;
- },
-
- /**
- * create touchList depending on the event
- * @param {Object} ev
- * @param {String} eventType
- * @return {Array} touches
- */
- getTouchList: function getTouchList(ev, eventType) {
- // get the fake pointerEvent touchlist
- if (GestureDetector.HAS_POINTEREVENTS) {
- return PointerEvent.getTouchList();
- }
-
- // get the touchlist
- if (ev.touches) {
- if (eventType == EVENT_MOVE) {
- return ev.touches;
- }
-
- var identifiers = [];
- var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches));
- var touchList = [];
-
- Utils.each(concat, function (touch) {
- if (Utils.inArray(identifiers, touch.identifier) === -1) {
- touchList.push(touch);
- }
- identifiers.push(touch.identifier);
- });
-
- return touchList;
- }
-
- // make fake touchList from mouse position
- ev.identifier = 1;
- return [ev];
- },
-
- /**
- * collect basic event data
- * @param {HTMLElement} element
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Array} touches
- * @param {Object} ev
- * @return {Object} ev
- */
- collectEventData: function collectEventData(element, eventType, touches, ev) {
- // find out pointerType
- var pointerType = POINTER_TOUCH;
- if (Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) {
- pointerType = POINTER_MOUSE;
- } else if (PointerEvent.matchType(POINTER_PEN, ev)) {
- pointerType = POINTER_PEN;
- }
-
- return {
- center: Utils.getCenter(touches),
- timeStamp: Date.now(),
- target: ev.target,
- touches: touches,
- eventType: eventType,
- pointerType: pointerType,
- srcEvent: ev,
-
- /**
- * prevent the browser default actions
- * mostly used to disable scrolling of the browser
- */
- preventDefault: function preventDefault() {
- var srcEvent = this.srcEvent;
- srcEvent.preventManipulation && srcEvent.preventManipulation();
- srcEvent.preventDefault && srcEvent.preventDefault();
- },
-
- /**
- * stop bubbling the event up to its parents
- */
- stopPropagation: function stopPropagation() {
- this.srcEvent.stopPropagation();
- },
-
- /**
- * immediately stop gesture detection
- * might be useful after a swipe was detected
- * @return {*}
- */
- stopDetect: function stopDetect() {
- return Detection.stopDetect();
- }
- };
- }
- };
-
- /**
- * @module GestureDetector
- *
- * @class PointerEvent
- * @static
- */
- PointerEvent = GestureDetector.PointerEvent = {
- /**
- * holds all pointers, by `identifier`
- * @property pointers
- * @type {Object}
- */
- pointers: {},
-
- /**
- * get the pointers as an array
- * @return {Array} touchlist
- */
- getTouchList: function getTouchList() {
- var touchlist = [];
- // we can use forEach since pointerEvents only is in IE10
- Utils.each(this.pointers, function (pointer) {
- touchlist.push(pointer);
- });
- return touchlist;
- },
-
- /**
- * update the position of a pointer
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Object} pointerEvent
- */
- updatePointer: function updatePointer(eventType, pointerEvent) {
- if (eventType == EVENT_END || eventType != EVENT_END && pointerEvent.buttons !== 1) {
- delete this.pointers[pointerEvent.pointerId];
- } else {
- pointerEvent.identifier = pointerEvent.pointerId;
- this.pointers[pointerEvent.pointerId] = pointerEvent;
- }
- },
-
- /**
- * check if ev matches pointertype
- * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN`
- * @param {PointerEvent} ev
- */
- matchType: function matchType(pointerType, ev) {
- if (!ev.pointerType) {
- return false;
- }
-
- var pt = ev.pointerType,
- types = {};
-
- types[POINTER_MOUSE] = pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE);
- types[POINTER_TOUCH] = pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH);
- types[POINTER_PEN] = pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN);
- return types[pointerType];
- },
-
- /**
- * reset the stored pointers
- */
- reset: function resetList() {
- this.pointers = {};
- }
- };
-
- /**
- * @module GestureDetector
- *
- * @class Detection
- * @static
- */
- Detection = GestureDetector.detection = {
- // contains all registered GestureDetector.gestures in the correct order
- gestures: [],
-
- // data of the current GestureDetector.gesture detection session
- current: null,
-
- // the previous GestureDetector.gesture session data
- // is a full clone of the previous gesture.current object
- previous: null,
-
- // when this becomes true, no gestures are fired
- stopped: false,
-
- /**
- * start GestureDetector.gesture detection
- * @param {GestureDetector.Instance} inst
- * @param {Object} eventData
- */
- startDetect: function startDetect(inst, eventData) {
- // already busy with a GestureDetector.gesture detection on an element
- if (this.current) {
- return;
- }
-
- this.stopped = false;
-
- // holds current session
- this.current = {
- inst: inst, // reference to GestureDetectorInstance we're working for
- startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc
- lastEvent: false, // last eventData
- lastCalcEvent: false, // last eventData for calculations.
- futureCalcEvent: false, // last eventData for calculations.
- lastCalcData: {}, // last lastCalcData
- name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc
- };
-
- this.detect(eventData);
- },
-
- /**
- * GestureDetector.gesture detection
- * @param {Object} eventData
- * @return {any}
- */
- detect: function detect(eventData) {
- if (!this.current || this.stopped) {
- return;
- }
-
- // extend event data with calculations about scale, distance etc
- eventData = this.extendEventData(eventData);
-
- // GestureDetector instance and instance options
- var inst = this.current.inst,
- instOptions = inst.options;
-
- // call GestureDetector.gesture handlers
- Utils.each(this.gestures, function triggerGesture(gesture) {
- // only when the instance options have enabled this gesture
- if (!this.stopped && inst.enabled && instOptions[gesture.name]) {
- gesture.handler.call(gesture, eventData, inst);
- }
- }, this);
-
- // store as previous event event
- if (this.current) {
- this.current.lastEvent = eventData;
- }
-
- if (eventData.eventType == EVENT_END) {
- this.stopDetect();
- }
-
- return eventData; // eslint-disable-line consistent-return
- },
-
- /**
- * clear the GestureDetector.gesture vars
- * this is called on endDetect, but can also be used when a final GestureDetector.gesture has been detected
- * to stop other GestureDetector.gestures from being fired
- */
- stopDetect: function stopDetect() {
- // clone current data to the store as the previous gesture
- // used for the double tap gesture, since this is an other gesture detect session
- this.previous = Utils.extend({}, this.current);
-
- // reset the current
- this.current = null;
- this.stopped = true;
- },
-
- /**
- * calculate velocity, angle and direction
- * @param {Object} ev
- * @param {Object} center
- * @param {Number} deltaTime
- * @param {Number} deltaX
- * @param {Number} deltaY
- */
- getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) {
- var cur = this.current,
- recalc = false,
- calcEv = cur.lastCalcEvent,
- calcData = cur.lastCalcData;
-
- if (calcEv && ev.timeStamp - calcEv.timeStamp > GestureDetector.CALCULATE_INTERVAL) {
- center = calcEv.center;
- deltaTime = ev.timeStamp - calcEv.timeStamp;
- deltaX = ev.center.clientX - calcEv.center.clientX;
- deltaY = ev.center.clientY - calcEv.center.clientY;
- recalc = true;
- }
-
- if (ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
- cur.futureCalcEvent = ev;
- }
-
- if (!cur.lastCalcEvent || recalc) {
- calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY);
- calcData.angle = Utils.getAngle(center, ev.center);
- calcData.direction = Utils.getDirection(center, ev.center);
-
- cur.lastCalcEvent = cur.futureCalcEvent || ev;
- cur.futureCalcEvent = ev;
- }
-
- ev.velocityX = calcData.velocity.x;
- ev.velocityY = calcData.velocity.y;
- ev.interimAngle = calcData.angle;
- ev.interimDirection = calcData.direction;
- },
-
- /**
- * extend eventData for GestureDetector.gestures
- * @param {Object} ev
- * @return {Object} ev
- */
- extendEventData: function extendEventData(ev) {
- var cur = this.current,
- startEv = cur.startEvent,
- lastEv = cur.lastEvent || startEv;
-
- // update the start touchlist to calculate the scale/rotation
- if (ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
- startEv.touches = [];
- Utils.each(ev.touches, function (touch) {
- startEv.touches.push({
- clientX: touch.clientX,
- clientY: touch.clientY
- });
- });
- }
-
- var deltaTime = ev.timeStamp - startEv.timeStamp,
- deltaX = ev.center.clientX - startEv.center.clientX,
- deltaY = ev.center.clientY - startEv.center.clientY;
-
- this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY);
-
- Utils.extend(ev, {
- startEvent: startEv,
-
- deltaTime: deltaTime,
- deltaX: deltaX,
- deltaY: deltaY,
-
- distance: Utils.getDistance(startEv.center, ev.center),
- angle: Utils.getAngle(startEv.center, ev.center),
- direction: Utils.getDirection(startEv.center, ev.center),
- scale: Utils.getScale(startEv.touches, ev.touches),
- rotation: Utils.getRotation(startEv.touches, ev.touches)
- });
-
- return ev;
- },
-
- /**
- * register new gesture
- * @param {Object} gesture object, see `gestures/` for documentation
- * @return {Array} gestures
- */
- register: function register(gesture) {
- // add an enable gesture options if there is no given
- var options = gesture.defaults || {};
- if (options[gesture.name] === undefined) {
- options[gesture.name] = true;
- }
-
- // extend GestureDetector default options with the GestureDetector.gesture options
- Utils.extend(GestureDetector.defaults, options, true);
-
- // set its index
- gesture.index = gesture.index || 1000;
-
- // add GestureDetector.gesture to the list
- this.gestures.push(gesture);
-
- // sort the list by index
- this.gestures.sort(function (a, b) {
- if (a.index < b.index) {
- return -1;
- }
- if (a.index > b.index) {
- return 1;
- }
- return 0;
- });
-
- return this.gestures;
- }
- };
-
- /**
- * @module GestureDetector
- */
-
- /**
- * create new GestureDetector instance
- * all methods should return the instance itself, so it is chainable.
- *
- * @class Instance
- * @constructor
- * @param {HTMLElement} element
- * @param {Object} [options={}] options are merged with `GestureDetector.defaults`
- * @return {GestureDetector.Instance}
- */
- GestureDetector.Instance = function (element, options) {
- var self = this;
- var listenerOptions = options && options.passive ? { passive: true } : undefined;
-
- // setup GestureDetectorJS window events and register all gestures
- // this also sets up the default options
- setup(listenerOptions);
-
- /**
- * @property element
- * @type {HTMLElement}
- */
- this.element = element;
-
- /**
- * @property enabled
- * @type {Boolean}
- * @protected
- */
- this.enabled = true;
-
- /**
- * options, merged with the defaults
- * options with an _ are converted to camelCase
- * @property options
- * @type {Object}
- */
- Utils.each(options, function (value, name) {
- delete options[name];
- options[Utils.toCamelCase(name)] = value;
- });
-
- this.options = Utils.extend(Utils.extend({}, GestureDetector.defaults), options || {});
- this.options.listenerOptions = listenerOptions;
-
- // add some css to the element to prevent the browser from doing its native behavior
- if (this.options.behavior) {
- Utils.toggleBehavior(this.element, this.options.behavior, true);
- }
-
- /**
- * event start handler on the element to start the detection
- * @property eventStartHandler
- * @type {Object}
- */
- this.eventStartHandler = Event.onTouch(element, EVENT_START, function (ev) {
- if (self.enabled && ev.eventType == EVENT_START) {
- Detection.startDetect(self, ev);
- } else if (ev.eventType == EVENT_TOUCH) {
- Detection.detect(ev);
- }
- }, listenerOptions);
-
- /**
- * keep a list of user event handlers which needs to be removed when calling 'dispose'
- * @property eventHandlers
- * @type {Array}
- */
- this.eventHandlers = [];
- };
-
- GestureDetector.Instance.prototype = {
- /**
- * @method on
- * @signature on(gestures, handler)
- * @description
- * [en]Adds an event handler for a gesture. Available gestures are: drag, dragleft, dragright, dragup, dragdown, hold, release, swipe, swipeleft, swiperight, swipeup, swipedown, tap, doubletap, touch, transform, pinch, pinchin, pinchout and rotate. [/en]
- * [ja]ジェスチャに対するイベントハンドラを追加します。指定できるジェスチャ名は、drag dragleft dragright dragup dragdown hold release swipe swipeleft swiperight swipeup swipedown tap doubletap touch transform pinch pinchin pinchout rotate です。[/ja]
- * @param {String} gestures
- * [en]A space separated list of gestures.[/en]
- * [ja]検知するジェスチャ名を指定します。スペースで複数指定することができます。[/ja]
- * @param {Function} handler
- * [en]An event handling function.[/en]
- * [ja]イベントハンドラとなる関数オブジェクトを指定します。[/ja]
- */
- on: function onEvent(gestures, handler, opt) {
- var self = this;
-
- Event.on(self.element, gestures, handler, util.extend({}, self.options.listenerOptions, opt), function (type) {
- self.eventHandlers.push({ gesture: type, handler: handler });
- });
- return self;
- },
-
- /**
- * @method off
- * @signature off(gestures, handler)
- * @description
- * [en]Remove an event listener.[/en]
- * [ja]イベントリスナーを削除します。[/ja]
- * @param {String} gestures
- * [en]A space separated list of gestures.[/en]
- * [ja]ジェスチャ名を指定します。スペースで複数指定することができます。[/ja]
- * @param {Function} handler
- * [en]An event handling function.[/en]
- * [ja]イベントハンドラとなる関数オブジェクトを指定します。[/ja]
- */
- off: function offEvent(gestures, handler, opt) {
- var self = this;
-
- Event.off(self.element, gestures, handler, util.extend({}, self.options.listenerOptions, opt), function (type) {
- var index = Utils.inArray(self.eventHandlers, { gesture: type, handler: handler }, true);
- if (index >= 0) {
- self.eventHandlers.splice(index, 1);
- }
- });
- return self;
- },
-
- /**
- * trigger gesture event
- * @method trigger
- * @signature trigger(gesture, eventData)
- * @param {String} gesture
- * @param {Object} [eventData]
- */
- trigger: function triggerEvent(gesture, eventData) {
- // optional
- if (!eventData) {
- eventData = {};
- }
-
- // create DOM event
- var event = GestureDetector.DOCUMENT.createEvent('Event');
- event.initEvent(gesture, true, true);
- event.gesture = eventData;
-
- // trigger on the target if it is in the instance element,
- // this is for event delegation tricks
- var element = this.element;
- if (Utils.hasParent(eventData.target, element)) {
- element = eventData.target;
- }
-
- element.dispatchEvent(event);
- return this;
- },
-
- /**
- * @method enable
- * @signature enable(state)
- * @description
- * [en]Enable or disable gesture detection.[/en]
- * [ja]ジェスチャ検知を有効化/無効化します。[/ja]
- * @param {Boolean} state
- * [en]Specify if it should be enabled or not.[/en]
- * [ja]有効にするかどうかを指定します。[/ja]
- */
- enable: function enable(state) {
- this.enabled = state;
- return this;
- },
-
- /**
- * @method dispose
- * @signature dispose()
- * @description
- * [en]Remove and destroy all event handlers for this instance.[/en]
- * [ja]このインスタンスでのジェスチャの検知や、イベントハンドラを全て解除して廃棄します。[/ja]
- */
- dispose: function dispose() {
- var i, eh;
-
- // undo all changes made by stop_browser_behavior
- Utils.toggleBehavior(this.element, this.options.behavior, false);
-
- // unbind all custom event handlers
- for (i = -1; eh = this.eventHandlers[++i];) {
- // eslint-disable-line no-cond-assign
- Utils.off(this.element, eh.gesture, eh.handler);
- }
-
- this.eventHandlers = [];
-
- // unbind the start event listener
- Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler);
-
- return null;
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * Move with x fingers (default 1) around on the page.
- * Preventing the default browser behavior is a good way to improve feel and working.
- * ````
- * GestureDetectortime.on("drag", function(ev) {
- * console.log(ev);
- * ev.gesture.preventDefault();
- * });
- * ````
- *
- * @class Drag
- * @static
- */
- /**
- * @event drag
- * @param {Object} ev
- */
- /**
- * @event dragstart
- * @param {Object} ev
- */
- /**
- * @event dragend
- * @param {Object} ev
- */
- /**
- * @event drapleft
- * @param {Object} ev
- */
- /**
- * @event dragright
- * @param {Object} ev
- */
- /**
- * @event dragup
- * @param {Object} ev
- */
- /**
- * @event dragdown
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function (name) {
- var triggered = false;
-
- function dragGesture(ev, inst) {
- var cur = Detection.current;
-
- // max touches
- if (inst.options.dragMaxTouches > 0 && ev.touches.length > inst.options.dragMaxTouches) {
- return;
- }
-
- switch (ev.eventType) {
- case EVENT_START:
- triggered = false;
- break;
-
- case EVENT_MOVE:
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if (ev.distance < inst.options.dragMinDistance && cur.name != name) {
- return;
- }
-
- var startCenter = cur.startEvent.center;
-
- // we are dragging!
- if (cur.name != name) {
- cur.name = name;
- if (inst.options.dragDistanceCorrection && ev.distance > 0) {
- // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center.
- // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0.
- // It might be useful to save the original start point somewhere
- var factor = Math.abs(inst.options.dragMinDistance / ev.distance);
- startCenter.pageX += ev.deltaX * factor;
- startCenter.pageY += ev.deltaY * factor;
- startCenter.clientX += ev.deltaX * factor;
- startCenter.clientY += ev.deltaY * factor;
-
- // recalculate event data using new start point
- ev = Detection.extendEventData(ev);
- }
- }
-
- // lock drag to axis?
- if (cur.lastEvent.dragLockToAxis || inst.options.dragLockToAxis && inst.options.dragLockMinDistance <= ev.distance) {
- ev.dragLockToAxis = true;
- }
-
- // keep direction on the axis that the drag gesture started on
- var lastDirection = cur.lastEvent.direction;
- if (ev.dragLockToAxis && lastDirection !== ev.direction) {
- if (Utils.isVertical(lastDirection)) {
- ev.direction = ev.deltaY < 0 ? DIRECTION_UP : DIRECTION_DOWN;
- } else {
- ev.direction = ev.deltaX < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
- }
- }
-
- // first time, trigger dragstart event
- if (!triggered) {
- inst.trigger(name + 'start', ev);
- triggered = true;
- }
-
- // trigger events
- inst.trigger(name, ev);
- inst.trigger(name + ev.direction, ev);
-
- var isVertical = Utils.isVertical(ev.direction);
-
- // block the browser events
- if (inst.options.dragBlockVertical && isVertical || inst.options.dragBlockHorizontal && !isVertical) {
- ev.preventDefault();
- }
- break;
-
- case EVENT_RELEASE:
- if (triggered && ev.changedLength <= inst.options.dragMaxTouches) {
- inst.trigger(name + 'end', ev);
- triggered = false;
- }
- break;
-
- case EVENT_END:
- triggered = false;
- break;
- }
- }
-
- GestureDetector.gestures.Drag = {
- name: name,
- index: 50,
- handler: dragGesture,
- defaults: {
- /**
- * minimal movement that have to be made before the drag event gets triggered
- * @property dragMinDistance
- * @type {Number}
- * @default 10
- */
- dragMinDistance: 10,
-
- /**
- * Set dragDistanceCorrection to true to make the starting point of the drag
- * be calculated from where the drag was triggered, not from where the touch started.
- * Useful to avoid a jerk-starting drag, which can make fine-adjustments
- * through dragging difficult, and be visually unappealing.
- * @property dragDistanceCorrection
- * @type {Boolean}
- * @default true
- */
- dragDistanceCorrection: true,
-
- /**
- * set 0 for unlimited, but this can conflict with transform
- * @property dragMaxTouches
- * @type {Number}
- * @default 1
- */
- dragMaxTouches: 1,
-
- /**
- * prevent default browser behavior when dragging occurs
- * be careful with it, it makes the element a blocking element
- * when you are using the drag gesture, it is a good practice to set this true
- * @property dragBlockHorizontal
- * @type {Boolean}
- * @default false
- */
- dragBlockHorizontal: false,
-
- /**
- * same as `dragBlockHorizontal`, but for vertical movement
- * @property dragBlockVertical
- * @type {Boolean}
- * @default false
- */
- dragBlockVertical: false,
-
- /**
- * dragLockToAxis keeps the drag gesture on the axis that it started on,
- * It disallows vertical directions if the initial direction was horizontal, and vice versa.
- * @property dragLockToAxis
- * @type {Boolean}
- * @default false
- */
- dragLockToAxis: false,
-
- /**
- * drag lock only kicks in when distance > dragLockMinDistance
- * This way, locking occurs only when the distance has become large enough to reliably determine the direction
- * @property dragLockMinDistance
- * @type {Number}
- * @default 25
- */
- dragLockMinDistance: 25
- }
- };
- })('drag');
-
- /**
- * @module gestures
- */
- /**
- * trigger a simple gesture event, so you can do anything in your handler.
- * only usable if you know what your doing...
- *
- * @class Gesture
- * @static
- */
- /**
- * @event gesture
- * @param {Object} ev
- */
- GestureDetector.gestures.Gesture = {
- name: 'gesture',
- index: 1337,
- handler: function releaseGesture(ev, inst) {
- inst.trigger(this.name, ev);
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * Touch stays at the same place for x time
- *
- * @class Hold
- * @static
- */
- /**
- * @event hold
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function (name) {
- var timer;
-
- function holdGesture(ev, inst) {
- var options = inst.options,
- current = Detection.current;
-
- switch (ev.eventType) {
- case EVENT_START:
- clearTimeout(timer);
-
- // set the gesture so we can check in the timeout if it still is
- current.name = name;
-
- // set timer and if after the timeout it still is hold,
- // we trigger the hold event
- timer = setTimeout(function () {
- if (current && current.name == name) {
- inst.trigger(name, ev);
- }
- }, options.holdTimeout);
- break;
-
- case EVENT_MOVE:
- if (ev.distance > options.holdThreshold) {
- clearTimeout(timer);
- }
- break;
-
- case EVENT_RELEASE:
- clearTimeout(timer);
- break;
- }
- }
-
- GestureDetector.gestures.Hold = {
- name: name,
- index: 10,
- defaults: {
- /**
- * @property holdTimeout
- * @type {Number}
- * @default 500
- */
- holdTimeout: 500,
-
- /**
- * movement allowed while holding
- * @property holdThreshold
- * @type {Number}
- * @default 2
- */
- holdThreshold: 2
- },
- handler: holdGesture
- };
- })('hold');
-
- /**
- * @module gestures
- */
- /**
- * when a touch is being released from the page
- *
- * @class Release
- * @static
- */
- /**
- * @event release
- * @param {Object} ev
- */
- GestureDetector.gestures.Release = {
- name: 'release',
- index: Infinity,
- handler: function releaseGesture(ev, inst) {
- if (ev.eventType == EVENT_RELEASE) {
- inst.trigger(this.name, ev);
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * triggers swipe events when the end velocity is above the threshold
- * for best usage, set `preventDefault` (on the drag gesture) to `true`
- * ````
- * GestureDetectortime.on("dragleft swipeleft", function(ev) {
- * console.log(ev);
- * ev.gesture.preventDefault();
- * });
- * ````
- *
- * @class Swipe
- * @static
- */
- /**
- * @event swipe
- * @param {Object} ev
- */
- /**
- * @event swipeleft
- * @param {Object} ev
- */
- /**
- * @event swiperight
- * @param {Object} ev
- */
- /**
- * @event swipeup
- * @param {Object} ev
- */
- /**
- * @event swipedown
- * @param {Object} ev
- */
- GestureDetector.gestures.Swipe = {
- name: 'swipe',
- index: 40,
- defaults: {
- /**
- * @property swipeMinTouches
- * @type {Number}
- * @default 1
- */
- swipeMinTouches: 1,
-
- /**
- * @property swipeMaxTouches
- * @type {Number}
- * @default 1
- */
- swipeMaxTouches: 1,
-
- /**
- * horizontal swipe velocity
- * @property swipeVelocityX
- * @type {Number}
- * @default 0.6
- */
- swipeVelocityX: 0.6,
-
- /**
- * vertical swipe velocity
- * @property swipeVelocityY
- * @type {Number}
- * @default 0.6
- */
- swipeVelocityY: 0.6
- },
-
- handler: function swipeGesture(ev, inst) {
- if (ev.eventType == EVENT_RELEASE) {
- var touches = ev.touches.length,
- options = inst.options;
-
- // max touches
- if (touches < options.swipeMinTouches || touches > options.swipeMaxTouches) {
- return;
- }
-
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if (ev.velocityX > options.swipeVelocityX || ev.velocityY > options.swipeVelocityY) {
- // trigger swipe events
- inst.trigger(this.name, ev);
- inst.trigger(this.name + ev.direction, ev);
- }
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * Single tap and a double tap on a place
- *
- * @class Tap
- * @static
- */
- /**
- * @event tap
- * @param {Object} ev
- */
- /**
- * @event doubletap
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function (name) {
- var hasMoved = false;
-
- function tapGesture(ev, inst) {
- var options = inst.options,
- current = Detection.current,
- prev = Detection.previous,
- sincePrev,
- didDoubleTap;
-
- switch (ev.eventType) {
- case EVENT_START:
- hasMoved = false;
- break;
-
- case EVENT_MOVE:
- hasMoved = hasMoved || ev.distance > options.tapMaxDistance;
- break;
-
- case EVENT_END:
- if (!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) {
- // previous gesture, for the double tap since these are two different gesture detections
- sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp;
- didDoubleTap = false;
-
- // check if double tap
- if (prev && prev.name == name && sincePrev && sincePrev < options.doubleTapInterval && ev.distance < options.doubleTapDistance) {
- inst.trigger('doubletap', ev);
- didDoubleTap = true;
- }
-
- // do a single tap
- if (!didDoubleTap || options.tapAlways) {
- current.name = name;
- inst.trigger(current.name, ev);
- }
- }
- break;
- }
- }
-
- GestureDetector.gestures.Tap = {
- name: name,
- index: 100,
- handler: tapGesture,
- defaults: {
- /**
- * max time of a tap, this is for the slow tappers
- * @property tapMaxTime
- * @type {Number}
- * @default 250
- */
- tapMaxTime: 250,
-
- /**
- * max distance of movement of a tap, this is for the slow tappers
- * @property tapMaxDistance
- * @type {Number}
- * @default 10
- */
- tapMaxDistance: 10,
-
- /**
- * always trigger the `tap` event, even while double-tapping
- * @property tapAlways
- * @type {Boolean}
- * @default true
- */
- tapAlways: true,
-
- /**
- * max distance between two taps
- * @property doubleTapDistance
- * @type {Number}
- * @default 20
- */
- doubleTapDistance: 20,
-
- /**
- * max time between two taps
- * @property doubleTapInterval
- * @type {Number}
- * @default 300
- */
- doubleTapInterval: 300
- }
- };
- })('tap');
-
- /**
- * @module gestures
- */
- /**
- * when a touch is being touched at the page
- *
- * @class Touch
- * @static
- */
- /**
- * @event touch
- * @param {Object} ev
- */
- GestureDetector.gestures.Touch = {
- name: 'touch',
- index: -Infinity,
- defaults: {
- /**
- * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page,
- * but it improves gestures like transforming and dragging.
- * be careful with using this, it can be very annoying for users to be stuck on the page
- * @property preventDefault
- * @type {Boolean}
- * @default false
- */
- preventDefault: false,
-
- /**
- * disable mouse events, so only touch (or pen!) input triggers events
- * @property preventMouse
- * @type {Boolean}
- * @default false
- */
- preventMouse: false
- },
- handler: function touchGesture(ev, inst) {
- if (inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) {
- ev.stopDetect();
- return;
- }
-
- if (inst.options.preventDefault) {
- ev.preventDefault();
- }
-
- if (ev.eventType == EVENT_TOUCH) {
- inst.trigger('touch', ev);
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * User want to scale or rotate with 2 fingers
- * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the
- * `preventDefault` option.
- *
- * @class Transform
- * @static
- */
- /**
- * @event transform
- * @param {Object} ev
- */
- /**
- * @event transformstart
- * @param {Object} ev
- */
- /**
- * @event transformend
- * @param {Object} ev
- */
- /**
- * @event pinchin
- * @param {Object} ev
- */
- /**
- * @event pinchout
- * @param {Object} ev
- */
- /**
- * @event rotate
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function (name) {
- var triggered = false;
-
- function transformGesture(ev, inst) {
- switch (ev.eventType) {
- case EVENT_START:
- triggered = false;
- break;
-
- case EVENT_MOVE:
- // at least multitouch
- if (ev.touches.length < 2) {
- return;
- }
-
- var scaleThreshold = Math.abs(1 - ev.scale);
- var rotationThreshold = Math.abs(ev.rotation);
-
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if (scaleThreshold < inst.options.transformMinScale && rotationThreshold < inst.options.transformMinRotation) {
- return;
- }
-
- // we are transforming!
- Detection.current.name = name;
-
- // first time, trigger dragstart event
- if (!triggered) {
- inst.trigger(name + 'start', ev);
- triggered = true;
- }
-
- inst.trigger(name, ev); // basic transform event
-
- // trigger rotate event
- if (rotationThreshold > inst.options.transformMinRotation) {
- inst.trigger('rotate', ev);
- }
-
- // trigger pinch event
- if (scaleThreshold > inst.options.transformMinScale) {
- inst.trigger('pinch', ev);
- inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev);
- }
- break;
-
- case EVENT_RELEASE:
- if (triggered && ev.changedLength < 2) {
- inst.trigger(name + 'end', ev);
- triggered = false;
- }
- break;
- }
- }
-
- GestureDetector.gestures.Transform = {
- name: name,
- index: 45,
- defaults: {
- /**
- * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
- * @property transformMinScale
- * @type {Number}
- * @default 0.01
- */
- transformMinScale: 0.01,
-
- /**
- * rotation in degrees
- * @property transformMinRotation
- * @type {Number}
- * @default 1
- */
- transformMinRotation: 1
- },
-
- handler: transformGesture
- };
- })('transform');
-
- export default GestureDetector;
|