Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
151 - 1
/*!
2
 * @fileOverview TouchSwipe - jQuery Plugin
3
 * @version 1.6.18
4
 *
5
 * @author Matt Bryson http://www.github.com/mattbryson
6
 * @see https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
7
 * @see http://labs.rampinteractive.co.uk/touchSwipe/
8
 * @see http://plugins.jquery.com/project/touchSwipe
9
 * @license
10
 * Copyright (c) 2010-2015 Matt Bryson
11
 * Dual licensed under the MIT or GPL Version 2 licenses.
12
 *
13
 */
14
 
15
/*
16
 *
17
 * Changelog
18
 * $Date: 2010-12-12 (Wed, 12 Dec 2010) $
19
 * $version: 1.0.0
20
 * $version: 1.0.1 - removed multibyte comments
21
 *
22
 * $Date: 2011-21-02 (Mon, 21 Feb 2011) $
23
 * $version: 1.1.0 	- added allowPageScroll property to allow swiping and scrolling of page
24
 *					- changed handler signatures so one handler can be used for multiple events
25
 * $Date: 2011-23-02 (Wed, 23 Feb 2011) $
26
 * $version: 1.2.0 	- added click handler. This is fired if the user simply clicks and does not swipe. The event object and click target are passed to handler.
27
 *					- If you use the http://code.google.com/p/jquery-ui-for-ipad-and-iphone/ plugin, you can also assign jQuery mouse events to children of a touchSwipe object.
28
 * $version: 1.2.1 	- removed console log!
29
 *
30
 * $version: 1.2.2 	- Fixed bug where scope was not preserved in callback methods.
31
 *
32
 * $Date: 2011-28-04 (Thurs, 28 April 2011) $
33
 * $version: 1.2.4 	- Changed licence terms to be MIT or GPL inline with jQuery. Added check for support of touch events to stop non compatible browsers erroring.
34
 *
35
 * $Date: 2011-27-09 (Tues, 27 September 2011) $
36
 * $version: 1.2.5 	- Added support for testing swipes with mouse on desktop browser (thanks to https://github.com/joelhy)
37
 *
38
 * $Date: 2012-14-05 (Mon, 14 May 2012) $
39
 * $version: 1.2.6 	- Added timeThreshold between start and end touch, so user can ignore slow swipes (thanks to Mark Chase). Default is null, all swipes are detected
40
 *
41
 * $Date: 2012-05-06 (Tues, 05 June 2012) $
42
 * $version: 1.2.7 	- Changed time threshold to have null default for backwards compatibility. Added duration param passed back in events, and refactored how time is handled.
43
 *
44
 * $Date: 2012-05-06 (Tues, 05 June 2012) $
45
 * $version: 1.2.8 	- Added the possibility to return a value like null or false in the trigger callback. In that way we can control when the touch start/move should take effect or not (simply by returning in some cases return null; or return false;) This effects the ontouchstart/ontouchmove event.
46
 *
47
 * $Date: 2012-06-06 (Wed, 06 June 2012) $
48
 * $version: 1.3.0 	- Refactored whole plugin to allow for methods to be executed, as well as exposed defaults for user override. Added 'enable', 'disable', and 'destroy' methods
49
 *
50
 * $Date: 2012-05-06 (Fri, 05 June 2012) $
51
 * $version: 1.3.1 	- Bug fixes  - bind() with false as last argument is no longer supported in jQuery 1.6, also, if you just click, the duration is now returned correctly.
52
 *
53
 * $Date: 2012-29-07 (Sun, 29 July 2012) $
54
 * $version: 1.3.2	- Added fallbackToMouseEvents option to NOT capture mouse events on non touch devices.
55
 * 			- Added "all" fingers value to the fingers property, so any combination of fingers triggers the swipe, allowing event handlers to check the finger count
56
 *
57
 * $Date: 2012-09-08 (Thurs, 9 Aug 2012) $
58
 * $version: 1.3.3	- Code tidy prep for minefied version
59
 *
60
 * $Date: 2012-04-10 (wed, 4 Oct 2012) $
61
 * $version: 1.4.0	- Added pinch support, pinchIn and pinchOut
62
 *
63
 * $Date: 2012-11-10 (Thurs, 11 Oct 2012) $
64
 * $version: 1.5.0	- Added excludedElements, a jquery selector that specifies child elements that do NOT trigger swipes. By default, this is .noSwipe
65
 *
66
 * $Date: 2012-22-10 (Mon, 22 Oct 2012) $
67
 * $version: 1.5.1	- Fixed bug with jQuery 1.8 and trailing comma in excludedElements
68
 *					- Fixed bug with IE and eventPreventDefault()
69
 * $Date: 2013-01-12 (Fri, 12 Jan 2013) $
70
 * $version: 1.6.0	- Fixed bugs with pinching, mainly when both pinch and swipe enabled, as well as adding time threshold for multifinger gestures, so releasing one finger beofre the other doesnt trigger as single finger gesture.
71
 *					- made the demo site all static local HTML pages so they can be run locally by a developer
72
 *					- added jsDoc comments and added documentation for the plugin
73
 *					- code tidy
74
 *					- added triggerOnTouchLeave property that will end the event when the user swipes off the element.
75
 * $Date: 2013-03-23 (Sat, 23 Mar 2013) $
76
 * $version: 1.6.1	- Added support for ie8 touch events
77
 * $version: 1.6.2	- Added support for events binding with on / off / bind in jQ for all callback names.
78
 *                   - Deprecated the 'click' handler in favour of tap.
79
 *                   - added cancelThreshold property
80
 *                   - added option method to update init options at runtime
81
 * $version 1.6.3    - added doubletap, longtap events and longTapThreshold, doubleTapThreshold property
82
 *
83
 * $Date: 2013-04-04 (Thurs, 04 April 2013) $
84
 * $version 1.6.4    - Fixed bug with cancelThreshold introduced in 1.6.3, where swipe status no longer fired start event, and stopped once swiping back.
85
 *
86
 * $Date: 2013-08-24 (Sat, 24 Aug 2013) $
87
 * $version 1.6.5    - Merged a few pull requests fixing various bugs, added AMD support.
88
 *
89
 * $Date: 2014-06-04 (Wed, 04 June 2014) $
90
 * $version 1.6.6 	- Merge of pull requests.
91
 *    				- IE10 touch support
92
 *    				- Only prevent default event handling on valid swipe
93
 *    				- Separate license/changelog comment
94
 *    				- Detect if the swipe is valid at the end of the touch event.
95
 *    				- Pass fingerdata to event handlers.
96
 *    				- Add 'hold' gesture
97
 *    				- Be more tolerant about the tap distance
98
 *    				- Typos and minor fixes
99
 *
100
 * $Date: 2015-22-01 (Thurs, 22 Jan 2015) $
101
 * $version 1.6.7    - Added patch from https://github.com/mattbryson/TouchSwipe-Jquery-Plugin/issues/206 to fix memory leak
102
 *
103
 * $Date: 2015-2-2 (Mon, 2 Feb 2015) $
104
 * $version 1.6.8    - Added preventDefaultEvents option to proxy events regardless.
105
 *					- Fixed issue with swipe and pinch not triggering at the same time
106
 *
107
 * $Date: 2015-9-6 (Tues, 9 June 2015) $
108
 * $version 1.6.9    - Added PR from jdalton/hybrid to fix pointer events
109
 *					- Added scrolling demo
110
 *					- Added version property to plugin
111
 *
112
 * $Date: 2015-1-10 (Wed, 1 October 2015) $
113
 * $version 1.6.10    - Added PR from beatspace to fix tap events
114
 * $version 1.6.11    - Added PRs from indri-indri ( Doc tidyup), kkirsche ( Bower tidy up ), UziTech (preventDefaultEvents fixes )
115
 *					 - Allowed setting multiple options via .swipe("options", options_hash) and more simply .swipe(options_hash) or exisitng instances
116
 * $version 1.6.12    - Fixed bug with multi finger releases above 2 not triggering events
117
 *
118
 * $Date: 2015-12-18 (Fri, 18 December 2015) $
119
 * $version 1.6.13    - Added PRs
120
 *                    - Fixed #267 allowPageScroll not working correctly
121
 * $version 1.6.14    - Fixed #220 / #248 doubletap not firing with swipes, #223 commonJS compatible
122
 * $version 1.6.15    - More bug fixes
123
 *
124
 * $Date: 2016-04-29 (Fri, 29 April 2016) $
125
 * $version 1.6.16    - Swipes with 0 distance now allow default events to trigger.  So tapping any form elements or A tags will allow default interaction, but swiping will trigger a swipe.
126
                        Removed the a, input, select etc from the excluded Children list as the 0 distance tap solves that issue.
127
* $Date: 2016-05-19  (Fri, 29 April 2016) $
128
* $version 1.6.17     - Fixed context issue when calling instance methods via $("selector").swipe("method");
129
* $version 1.6.18     - now honors fallbackToMouseEvents=false for MS Pointer events when a Mouse is used.
130
 
131
 */
132
 
133
/**
134
 * See (http://jquery.com/).
135
 * @name $
136
 * @class
137
 * See the jQuery Library  (http://jquery.com/) for full details.  This just
138
 * documents the function and classes that are added to jQuery by this plug-in.
139
 */
140
 
141
/**
142
 * See (http://jquery.com/)
143
 * @name fn
144
 * @class
145
 * See the jQuery Library  (http://jquery.com/) for full details.  This just
146
 * documents the function and classes that are added to jQuery by this plug-in.
147
 * @memberOf $
148
 */
149
 
150
 
151
(function(factory) {
152
  if (typeof define === 'function' && define.amd && define.amd.jQuery) {
153
    // AMD. Register as anonymous module.
154
    define(['jquery'], factory);
155
  } else if (typeof module !== 'undefined' && module.exports) {
156
    // CommonJS Module
157
    factory(require("jquery"));
158
  } else {
159
    // Browser globals.
160
    factory(jQuery);
161
  }
162
}(function($) {
163
  "use strict";
164
 
165
  //Constants
166
  var VERSION = "1.6.18",
167
    LEFT = "left",
168
    RIGHT = "right",
169
    UP = "up",
170
    DOWN = "down",
171
    IN = "in",
172
    OUT = "out",
173
 
174
    NONE = "none",
175
    AUTO = "auto",
176
 
177
    SWIPE = "swipe",
178
    PINCH = "pinch",
179
    TAP = "tap",
180
    DOUBLE_TAP = "doubletap",
181
    LONG_TAP = "longtap",
182
    HOLD = "hold",
183
 
184
    HORIZONTAL = "horizontal",
185
    VERTICAL = "vertical",
186
 
187
    ALL_FINGERS = "all",
188
 
189
    DOUBLE_TAP_THRESHOLD = 10,
190
 
191
    PHASE_START = "start",
192
    PHASE_MOVE = "move",
193
    PHASE_END = "end",
194
    PHASE_CANCEL = "cancel",
195
 
196
    SUPPORTS_TOUCH = 'ontouchstart' in window,
197
 
198
    SUPPORTS_POINTER_IE10 = window.navigator.msPointerEnabled && !window.PointerEvent && !SUPPORTS_TOUCH,
199
 
200
    SUPPORTS_POINTER = (window.PointerEvent || window.navigator.msPointerEnabled) && !SUPPORTS_TOUCH,
201
 
202
    PLUGIN_NS = 'TouchSwipe';
203
 
204
 
205
 
206
  /**
207
  * The default configuration, and available options to configure touch swipe with.
208
  * You can set the default values by updating any of the properties prior to instantiation.
209
  * @name $.fn.swipe.defaults
210
  * @namespace
211
  * @property {int} [fingers=1] The number of fingers to detect in a swipe. Any swipes that do not meet this requirement will NOT trigger swipe handlers.
212
  * @property {int} [threshold=75] The number of pixels that the user must move their finger by before it is considered a swipe.
213
  * @property {int} [cancelThreshold=null] The number of pixels that the user must move their finger back from the original swipe direction to cancel the gesture.
214
  * @property {int} [pinchThreshold=20] The number of pixels that the user must pinch their finger by before it is considered a pinch.
215
  * @property {int} [maxTimeThreshold=null] Time, in milliseconds, between touchStart and touchEnd must NOT exceed in order to be considered a swipe.
216
  * @property {int} [fingerReleaseThreshold=250] Time in milliseconds between releasing multiple fingers.  If 2 fingers are down, and are released one after the other, if they are within this threshold, it counts as a simultaneous release.
217
  * @property {int} [longTapThreshold=500] Time in milliseconds between tap and release for a long tap
218
  * @property {int} [doubleTapThreshold=200] Time in milliseconds between 2 taps to count as a double tap
219
  * @property {function} [swipe=null] A handler to catch all swipes. See {@link $.fn.swipe#event:swipe}
220
  * @property {function} [swipeLeft=null] A handler that is triggered for "left" swipes. See {@link $.fn.swipe#event:swipeLeft}
221
  * @property {function} [swipeRight=null] A handler that is triggered for "right" swipes. See {@link $.fn.swipe#event:swipeRight}
222
  * @property {function} [swipeUp=null] A handler that is triggered for "up" swipes. See {@link $.fn.swipe#event:swipeUp}
223
  * @property {function} [swipeDown=null] A handler that is triggered for "down" swipes. See {@link $.fn.swipe#event:swipeDown}
224
  * @property {function} [swipeStatus=null] A handler triggered for every phase of the swipe. See {@link $.fn.swipe#event:swipeStatus}
225
  * @property {function} [pinchIn=null] A handler triggered for pinch in events. See {@link $.fn.swipe#event:pinchIn}
226
  * @property {function} [pinchOut=null] A handler triggered for pinch out events. See {@link $.fn.swipe#event:pinchOut}
227
  * @property {function} [pinchStatus=null] A handler triggered for every phase of a pinch. See {@link $.fn.swipe#event:pinchStatus}
228
  * @property {function} [tap=null] A handler triggered when a user just taps on the item, rather than swipes it. If they do not move, tap is triggered, if they do move, it is not.
229
  * @property {function} [doubleTap=null] A handler triggered when a user double taps on the item. The delay between taps can be set with the doubleTapThreshold property. See {@link $.fn.swipe.defaults#doubleTapThreshold}
230
  * @property {function} [longTap=null] A handler triggered when a user long taps on the item. The delay between start and end can be set with the longTapThreshold property. See {@link $.fn.swipe.defaults#longTapThreshold}
231
  * @property (function) [hold=null] A handler triggered when a user reaches longTapThreshold on the item. See {@link $.fn.swipe.defaults#longTapThreshold}
232
  * @property {boolean} [triggerOnTouchEnd=true] If true, the swipe events are triggered when the touch end event is received (user releases finger).  If false, it will be triggered on reaching the threshold, and then cancel the touch event automatically.
233
  * @property {boolean} [triggerOnTouchLeave=false] If true, then when the user leaves the swipe object, the swipe will end and trigger appropriate handlers.
234
  * @property {string|undefined} [allowPageScroll='auto'] How the browser handles page scrolls when the user is swiping on a touchSwipe object. See {@link $.fn.swipe.pageScroll}.  <br/><br/>
235
  									<code>"auto"</code> : all undefined swipes will cause the page to scroll in that direction. <br/>
236
  									<code>"none"</code> : the page will not scroll when user swipes. <br/>
237
  									<code>"horizontal"</code> : will force page to scroll on horizontal swipes. <br/>
238
  									<code>"vertical"</code> : will force page to scroll on vertical swipes. <br/>
239
  * @property {boolean} [fallbackToMouseEvents=true] If true mouse events are used when run on a non touch device, false will stop swipes being triggered by mouse events on non touch devices.
240
  * @property {string} [excludedElements=".noSwipe"] A jquery selector that specifies child elements that do NOT trigger swipes. By default this excludes elements with the class .noSwipe .
241
  * @property {boolean} [preventDefaultEvents=true] by default default events are cancelled, so the page doesn't move.  You can disable this so both native events fire as well as your handlers.
242
 
243
  */
244
  var defaults = {
245
    fingers: 1,
246
    threshold: 75,
247
    cancelThreshold: null,
248
    pinchThreshold: 20,
249
    maxTimeThreshold: null,
250
    fingerReleaseThreshold: 250,
251
    longTapThreshold: 500,
252
    doubleTapThreshold: 200,
253
    swipe: null,
254
    swipeLeft: null,
255
    swipeRight: null,
256
    swipeUp: null,
257
    swipeDown: null,
258
    swipeStatus: null,
259
    pinchIn: null,
260
    pinchOut: null,
261
    pinchStatus: null,
262
    click: null, //Deprecated since 1.6.2
263
    tap: null,
264
    doubleTap: null,
265
    longTap: null,
266
    hold: null,
267
    triggerOnTouchEnd: true,
268
    triggerOnTouchLeave: false,
269
    allowPageScroll: "auto",
270
    fallbackToMouseEvents: true,
271
    excludedElements: ".noSwipe",
272
    preventDefaultEvents: true
273
  };
274
 
275
 
276
 
277
  /**
278
   * Applies TouchSwipe behaviour to one or more jQuery objects.
279
   * The TouchSwipe plugin can be instantiated via this method, or methods within
280
   * TouchSwipe can be executed via this method as per jQuery plugin architecture.
281
   * An existing plugin can have its options changed simply by re calling .swipe(options)
282
   * @see TouchSwipe
283
   * @class
284
   * @param {Mixed} method If the current DOMNode is a TouchSwipe object, and <code>method</code> is a TouchSwipe method, then
285
   * the <code>method</code> is executed, and any following arguments are passed to the TouchSwipe method.
286
   * If <code>method</code> is an object, then the TouchSwipe class is instantiated on the current DOMNode, passing the
287
   * configuration properties defined in the object. See TouchSwipe
288
   *
289
   */
290
  $.fn.swipe = function(method) {
291
    var $this = $(this),
292
      plugin = $this.data(PLUGIN_NS);
293
 
294
    //Check if we are already instantiated and trying to execute a method
295
    if (plugin && typeof method === 'string') {
296
      if (plugin[method]) {
297
        return plugin[method].apply(plugin, Array.prototype.slice.call(arguments, 1));
298
      } else {
299
        $.error('Method ' + method + ' does not exist on jQuery.swipe');
300
      }
301
    }
302
 
303
    //Else update existing plugin with new options hash
304
    else if (plugin && typeof method === 'object') {
305
      plugin['option'].apply(plugin, arguments);
306
    }
307
 
308
    //Else not instantiated and trying to pass init object (or nothing)
309
    else if (!plugin && (typeof method === 'object' || !method)) {
310
      return init.apply(this, arguments);
311
    }
312
 
313
    return $this;
314
  };
315
 
316
  /**
317
   * The version of the plugin
318
   * @readonly
319
   */
320
  $.fn.swipe.version = VERSION;
321
 
322
 
323
 
324
  //Expose our defaults so a user could override the plugin defaults
325
  $.fn.swipe.defaults = defaults;
326
 
327
  /**
328
   * The phases that a touch event goes through.  The <code>phase</code> is passed to the event handlers.
329
   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
330
   * @namespace
331
   * @readonly
332
   * @property {string} PHASE_START Constant indicating the start phase of the touch event. Value is <code>"start"</code>.
333
   * @property {string} PHASE_MOVE Constant indicating the move phase of the touch event. Value is <code>"move"</code>.
334
   * @property {string} PHASE_END Constant indicating the end phase of the touch event. Value is <code>"end"</code>.
335
   * @property {string} PHASE_CANCEL Constant indicating the cancel phase of the touch event. Value is <code>"cancel"</code>.
336
   */
337
  $.fn.swipe.phases = {
338
    PHASE_START: PHASE_START,
339
    PHASE_MOVE: PHASE_MOVE,
340
    PHASE_END: PHASE_END,
341
    PHASE_CANCEL: PHASE_CANCEL
342
  };
343
 
344
  /**
345
   * The direction constants that are passed to the event handlers.
346
   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
347
   * @namespace
348
   * @readonly
349
   * @property {string} LEFT Constant indicating the left direction. Value is <code>"left"</code>.
350
   * @property {string} RIGHT Constant indicating the right direction. Value is <code>"right"</code>.
351
   * @property {string} UP Constant indicating the up direction. Value is <code>"up"</code>.
352
   * @property {string} DOWN Constant indicating the down direction. Value is <code>"cancel"</code>.
353
   * @property {string} IN Constant indicating the in direction. Value is <code>"in"</code>.
354
   * @property {string} OUT Constant indicating the out direction. Value is <code>"out"</code>.
355
   */
356
  $.fn.swipe.directions = {
357
    LEFT: LEFT,
358
    RIGHT: RIGHT,
359
    UP: UP,
360
    DOWN: DOWN,
361
    IN: IN,
362
    OUT: OUT
363
  };
364
 
365
  /**
366
   * The page scroll constants that can be used to set the value of <code>allowPageScroll</code> option
367
   * These properties are read only
368
   * @namespace
369
   * @readonly
370
   * @see $.fn.swipe.defaults#allowPageScroll
371
   * @property {string} NONE Constant indicating no page scrolling is allowed. Value is <code>"none"</code>.
372
   * @property {string} HORIZONTAL Constant indicating horizontal page scrolling is allowed. Value is <code>"horizontal"</code>.
373
   * @property {string} VERTICAL Constant indicating vertical page scrolling is allowed. Value is <code>"vertical"</code>.
374
   * @property {string} AUTO Constant indicating either horizontal or vertical will be allowed, depending on the swipe handlers registered. Value is <code>"auto"</code>.
375
   */
376
  $.fn.swipe.pageScroll = {
377
    NONE: NONE,
378
    HORIZONTAL: HORIZONTAL,
379
    VERTICAL: VERTICAL,
380
    AUTO: AUTO
381
  };
382
 
383
  /**
384
   * Constants representing the number of fingers used in a swipe.  These are used to set both the value of <code>fingers</code> in the
385
   * options object, as well as the value of the <code>fingers</code> event property.
386
   * These properties are read only, attempting to change them will not alter the values passed to the event handlers.
387
   * @namespace
388
   * @readonly
389
   * @see $.fn.swipe.defaults#fingers
390
   * @property {string} ONE Constant indicating 1 finger is to be detected / was detected. Value is <code>1</code>.
391
   * @property {string} TWO Constant indicating 2 fingers are to be detected / were detected. Value is <code>2</code>.
392
   * @property {string} THREE Constant indicating 3 finger are to be detected / were detected. Value is <code>3</code>.
393
   * @property {string} FOUR Constant indicating 4 finger are to be detected / were detected. Not all devices support this. Value is <code>4</code>.
394
   * @property {string} FIVE Constant indicating 5 finger are to be detected / were detected. Not all devices support this. Value is <code>5</code>.
395
   * @property {string} ALL Constant indicating any combination of finger are to be detected.  Value is <code>"all"</code>.
396
   */
397
  $.fn.swipe.fingers = {
398
    ONE: 1,
399
    TWO: 2,
400
    THREE: 3,
401
    FOUR: 4,
402
    FIVE: 5,
403
    ALL: ALL_FINGERS
404
  };
405
 
406
  /**
407
   * Initialise the plugin for each DOM element matched
408
   * This creates a new instance of the main TouchSwipe class for each DOM element, and then
409
   * saves a reference to that instance in the elements data property.
410
   * @internal
411
   */
412
  function init(options) {
413
    //Prep and extend the options
414
    if (options && (options.allowPageScroll === undefined && (options.swipe !== undefined || options.swipeStatus !== undefined))) {
415
      options.allowPageScroll = NONE;
416
    }
417
 
418
    //Check for deprecated options
419
    //Ensure that any old click handlers are assigned to the new tap, unless we have a tap
420
    if (options.click !== undefined && options.tap === undefined) {
421
      options.tap = options.click;
422
    }
423
 
424
    if (!options) {
425
      options = {};
426
    }
427
 
428
    //pass empty object so we dont modify the defaults
429
    options = $.extend({}, $.fn.swipe.defaults, options);
430
 
431
    //For each element instantiate the plugin
432
    return this.each(function() {
433
      var $this = $(this);
434
 
435
      //Check we havent already initialised the plugin
436
      var plugin = $this.data(PLUGIN_NS);
437
 
438
      if (!plugin) {
439
        plugin = new TouchSwipe(this, options);
440
        $this.data(PLUGIN_NS, plugin);
441
      }
442
    });
443
  }
444
 
445
  /**
446
   * Main TouchSwipe Plugin Class.
447
   * Do not use this to construct your TouchSwipe object, use the jQuery plugin method $.fn.swipe(); {@link $.fn.swipe}
448
   * @private
449
   * @name TouchSwipe
450
   * @param {DOMNode} element The HTML DOM object to apply to plugin to
451
   * @param {Object} options The options to configure the plugin with.  @link {$.fn.swipe.defaults}
452
   * @see $.fh.swipe.defaults
453
   * @see $.fh.swipe
454
   * @class
455
   */
456
  function TouchSwipe(element, options) {
457
 
458
    //take a local/instacne level copy of the options - should make it this.options really...
459
    var options = $.extend({}, options);
460
 
461
    var useTouchEvents = (SUPPORTS_TOUCH || SUPPORTS_POINTER || !options.fallbackToMouseEvents),
462
      START_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerDown' : 'pointerdown') : 'touchstart') : 'mousedown',
463
      MOVE_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerMove' : 'pointermove') : 'touchmove') : 'mousemove',
464
      END_EV = useTouchEvents ? (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerUp' : 'pointerup') : 'touchend') : 'mouseup',
465
      LEAVE_EV = useTouchEvents ? (SUPPORTS_POINTER ? 'mouseleave' : null) : 'mouseleave', //we manually detect leave on touch devices, so null event here
466
      CANCEL_EV = (SUPPORTS_POINTER ? (SUPPORTS_POINTER_IE10 ? 'MSPointerCancel' : 'pointercancel') : 'touchcancel');
467
 
468
 
469
 
470
    //touch properties
471
    var distance = 0,
472
      direction = null,
473
      currentDirection = null,
474
      duration = 0,
475
      startTouchesDistance = 0,
476
      endTouchesDistance = 0,
477
      pinchZoom = 1,
478
      pinchDistance = 0,
479
      pinchDirection = 0,
480
      maximumsMap = null;
481
 
482
 
483
 
484
    //jQuery wrapped element for this instance
485
    var $element = $(element);
486
 
487
    //Current phase of th touch cycle
488
    var phase = "start";
489
 
490
    // the current number of fingers being used.
491
    var fingerCount = 0;
492
 
493
    //track mouse points / delta
494
    var fingerData = {};
495
 
496
    //track times
497
    var startTime = 0,
498
      endTime = 0,
499
      previousTouchEndTime = 0,
500
      fingerCountAtRelease = 0,
501
      doubleTapStartTime = 0;
502
 
503
    //Timeouts
504
    var singleTapTimeout = null,
505
      holdTimeout = null;
506
 
507
    // Add gestures to all swipable areas if supported
508
    try {
509
      $element.on(START_EV, touchStart);
510
      $element.on(CANCEL_EV, touchCancel);
511
    } catch (e) {
512
      $.error('events not supported ' + START_EV + ',' + CANCEL_EV + ' on jQuery.swipe');
513
    }
514
 
515
    //
516
    //Public methods
517
    //
518
 
519
    /**
520
     * re-enables the swipe plugin with the previous configuration
521
     * @function
522
     * @name $.fn.swipe#enable
523
     * @return {DOMNode} The Dom element that was registered with TouchSwipe
524
     * @example $("#element").swipe("enable");
525
     */
526
    this.enable = function() {
527
      //Incase we are already enabled, clean up...
528
      this.disable();
529
      $element.on(START_EV, touchStart);
530
      $element.on(CANCEL_EV, touchCancel);
531
      return $element;
532
    };
533
 
534
    /**
535
     * disables the swipe plugin
536
     * @function
537
     * @name $.fn.swipe#disable
538
     * @return {DOMNode} The Dom element that is now registered with TouchSwipe
539
     * @example $("#element").swipe("disable");
540
     */
541
    this.disable = function() {
542
      removeListeners();
543
      return $element;
544
    };
545
 
546
    /**
547
     * Destroy the swipe plugin completely. To use any swipe methods, you must re initialise the plugin.
548
     * @function
549
     * @name $.fn.swipe#destroy
550
     * @example $("#element").swipe("destroy");
551
     */
552
    this.destroy = function() {
553
      removeListeners();
554
      $element.data(PLUGIN_NS, null);
555
      $element = null;
556
    };
557
 
558
 
559
    /**
560
     * Allows run time updating of the swipe configuration options.
561
     * @function
562
     * @name $.fn.swipe#option
563
     * @param {String} property The option property to get or set, or a has of multiple options to set
564
     * @param {Object} [value] The value to set the property to
565
     * @return {Object} If only a property name is passed, then that property value is returned. If nothing is passed the current options hash is returned.
566
     * @example $("#element").swipe("option", "threshold"); // return the threshold
567
     * @example $("#element").swipe("option", "threshold", 100); // set the threshold after init
568
     * @example $("#element").swipe("option", {threshold:100, fingers:3} ); // set multiple properties after init
569
     * @example $("#element").swipe({threshold:100, fingers:3} ); // set multiple properties after init - the "option" method is optional!
570
     * @example $("#element").swipe("option"); // Return the current options hash
571
     * @see $.fn.swipe.defaults
572
     *
573
     */
574
    this.option = function(property, value) {
575
 
576
      if (typeof property === 'object') {
577
        options = $.extend(options, property);
578
      } else if (options[property] !== undefined) {
579
        if (value === undefined) {
580
          return options[property];
581
        } else {
582
          options[property] = value;
583
        }
584
      } else if (!property) {
585
        return options;
586
      } else {
587
        $.error('Option ' + property + ' does not exist on jQuery.swipe.options');
588
      }
589
 
590
      return null;
591
    }
592
 
593
 
594
 
595
    //
596
    // Private methods
597
    //
598
 
599
    //
600
    // EVENTS
601
    //
602
    /**
603
     * Event handler for a touch start event.
604
     * Stops the default click event from triggering and stores where we touched
605
     * @inner
606
     * @param {object} jqEvent The normalised jQuery event object.
607
     */
608
    function touchStart(jqEvent) {
609
 
610
      //If we already in a touch event (a finger already in use) then ignore subsequent ones..
611
      if (getTouchInProgress()) {
612
        return;
613
      }
614
 
615
      //Check if this element matches any in the excluded elements selectors,  or its parent is excluded, if so, DON'T swipe
616
      if ($(jqEvent.target).closest(options.excludedElements, $element).length > 0) {
617
        return;
618
      }
619
 
620
      //As we use Jquery bind for events, we need to target the original event object
621
      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
622
      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
623
 
624
 
625
      //If we have a pointer event, whoes type is 'mouse' and we have said NO mouse events, then dont do anything.
626
      if(event.pointerType && event.pointerType=="mouse" && options.fallbackToMouseEvents==false) {
627
        return;
628
      };
629
 
630
      var ret,
631
        touches = event.touches,
632
        evt = touches ? touches[0] : event;
633
 
634
      phase = PHASE_START;
635
 
636
      //If we support touches, get the finger count
637
      if (touches) {
638
        // get the total number of fingers touching the screen
639
        fingerCount = touches.length;
640
      }
641
      //Else this is the desktop, so stop the browser from dragging content
642
      else if (options.preventDefaultEvents !== false) {
643
        jqEvent.preventDefault(); //call this on jq event so we are cross browser
644
      }
645
 
646
      //clear vars..
647
      distance = 0;
648
      direction = null;
649
      currentDirection=null;
650
      pinchDirection = null;
651
      duration = 0;
652
      startTouchesDistance = 0;
653
      endTouchesDistance = 0;
654
      pinchZoom = 1;
655
      pinchDistance = 0;
656
      maximumsMap = createMaximumsData();
657
      cancelMultiFingerRelease();
658
 
659
      //Create the default finger data
660
      createFingerData(0, evt);
661
 
662
      // check the number of fingers is what we are looking for, or we are capturing pinches
663
      if (!touches || (fingerCount === options.fingers || options.fingers === ALL_FINGERS) || hasPinches()) {
664
        // get the coordinates of the touch
665
        startTime = getTimeStamp();
666
 
667
        if (fingerCount == 2) {
668
          //Keep track of the initial pinch distance, so we can calculate the diff later
669
          //Store second finger data as start
670
          createFingerData(1, touches[1]);
671
          startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start);
672
        }
673
 
674
        if (options.swipeStatus || options.pinchStatus) {
675
          ret = triggerHandler(event, phase);
676
        }
677
      } else {
678
        //A touch with more or less than the fingers we are looking for, so cancel
679
        ret = false;
680
      }
681
 
682
      //If we have a return value from the users handler, then return and cancel
683
      if (ret === false) {
684
        phase = PHASE_CANCEL;
685
        triggerHandler(event, phase);
686
        return ret;
687
      } else {
688
        if (options.hold) {
689
          holdTimeout = setTimeout($.proxy(function() {
690
            //Trigger the event
691
            $element.trigger('hold', [event.target]);
692
            //Fire the callback
693
            if (options.hold) {
694
              ret = options.hold.call($element, event, event.target);
695
            }
696
          }, this), options.longTapThreshold);
697
        }
698
 
699
        setTouchInProgress(true);
700
      }
701
 
702
      return null;
703
    };
704
 
705
 
706
 
707
    /**
708
     * Event handler for a touch move event.
709
     * If we change fingers during move, then cancel the event
710
     * @inner
711
     * @param {object} jqEvent The normalised jQuery event object.
712
     */
713
    function touchMove(jqEvent) {
714
 
715
      //As we use Jquery bind for events, we need to target the original event object
716
      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
717
      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
718
 
719
      //If we are ending, cancelling, or within the threshold of 2 fingers being released, don't track anything..
720
      if (phase === PHASE_END || phase === PHASE_CANCEL || inMultiFingerRelease())
721
        return;
722
 
723
      var ret,
724
        touches = event.touches,
725
        evt = touches ? touches[0] : event;
726
 
727
 
728
      //Update the  finger data
729
      var currentFinger = updateFingerData(evt);
730
      endTime = getTimeStamp();
731
 
732
      if (touches) {
733
        fingerCount = touches.length;
734
      }
735
 
736
      if (options.hold) {
737
        clearTimeout(holdTimeout);
738
      }
739
 
740
      phase = PHASE_MOVE;
741
 
742
      //If we have 2 fingers get Touches distance as well
743
      if (fingerCount == 2) {
744
 
745
        //Keep track of the initial pinch distance, so we can calculate the diff later
746
        //We do this here as well as the start event, in case they start with 1 finger, and the press 2 fingers
747
        if (startTouchesDistance == 0) {
748
          //Create second finger if this is the first time...
749
          createFingerData(1, touches[1]);
750
 
751
          startTouchesDistance = endTouchesDistance = calculateTouchesDistance(fingerData[0].start, fingerData[1].start);
752
        } else {
753
          //Else just update the second finger
754
          updateFingerData(touches[1]);
755
 
756
          endTouchesDistance = calculateTouchesDistance(fingerData[0].end, fingerData[1].end);
757
          pinchDirection = calculatePinchDirection(fingerData[0].end, fingerData[1].end);
758
        }
759
 
760
        pinchZoom = calculatePinchZoom(startTouchesDistance, endTouchesDistance);
761
        pinchDistance = Math.abs(startTouchesDistance - endTouchesDistance);
762
      }
763
 
764
      if ((fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !touches || hasPinches()) {
765
 
766
        //The overall direction of the swipe. From start to now.
767
        direction = calculateDirection(currentFinger.start, currentFinger.end);
768
 
769
        //The immediate direction of the swipe, direction between the last movement and this one.
770
        currentDirection = calculateDirection(currentFinger.last, currentFinger.end);
771
 
772
        //Check if we need to prevent default event (page scroll / pinch zoom) or not
773
        validateDefaultEvent(jqEvent, currentDirection);
774
 
775
        //Distance and duration are all off the main finger
776
        distance = calculateDistance(currentFinger.start, currentFinger.end);
777
        duration = calculateDuration();
778
 
779
        //Cache the maximum distance we made in this direction
780
        setMaxDistance(direction, distance);
781
 
782
        //Trigger status handler
783
        ret = triggerHandler(event, phase);
784
 
785
 
786
        //If we trigger end events when threshold are met, or trigger events when touch leaves element
787
        if (!options.triggerOnTouchEnd || options.triggerOnTouchLeave) {
788
 
789
          var inBounds = true;
790
 
791
          //If checking if we leave the element, run the bounds check (we can use touchleave as its not supported on webkit)
792
          if (options.triggerOnTouchLeave) {
793
            var bounds = getbounds(this);
794
            inBounds = isInBounds(currentFinger.end, bounds);
795
          }
796
 
797
          //Trigger end handles as we swipe if thresholds met or if we have left the element if the user has asked to check these..
798
          if (!options.triggerOnTouchEnd && inBounds) {
799
            phase = getNextPhase(PHASE_MOVE);
800
          }
801
          //We end if out of bounds here, so set current phase to END, and check if its modified
802
          else if (options.triggerOnTouchLeave && !inBounds) {
803
            phase = getNextPhase(PHASE_END);
804
          }
805
 
806
          if (phase == PHASE_CANCEL || phase == PHASE_END) {
807
            triggerHandler(event, phase);
808
          }
809
        }
810
      } else {
811
        phase = PHASE_CANCEL;
812
        triggerHandler(event, phase);
813
      }
814
 
815
      if (ret === false) {
816
        phase = PHASE_CANCEL;
817
        triggerHandler(event, phase);
818
      }
819
    }
820
 
821
 
822
 
823
 
824
    /**
825
     * Event handler for a touch end event.
826
     * Calculate the direction and trigger events
827
     * @inner
828
     * @param {object} jqEvent The normalised jQuery event object.
829
     */
830
    function touchEnd(jqEvent) {
831
      //As we use Jquery bind for events, we need to target the original event object
832
      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
833
      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent,
834
        touches = event.touches;
835
 
836
      //If we are still in a touch with the device wait a fraction and see if the other finger comes up
837
      //if it does within the threshold, then we treat it as a multi release, not a single release and end the touch / swipe
838
      if (touches) {
839
        if (touches.length && !inMultiFingerRelease()) {
840
          startMultiFingerRelease(event);
841
          return true;
842
        } else if (touches.length && inMultiFingerRelease()) {
843
          return true;
844
        }
845
      }
846
 
847
      //If a previous finger has been released, check how long ago, if within the threshold, then assume it was a multifinger release.
848
      //This is used to allow 2 fingers to release fractionally after each other, whilst maintaining the event as containing 2 fingers, not 1
849
      if (inMultiFingerRelease()) {
850
        fingerCount = fingerCountAtRelease;
851
      }
852
 
853
      //Set end of swipe
854
      endTime = getTimeStamp();
855
 
856
      //Get duration incase move was never fired
857
      duration = calculateDuration();
858
 
859
      //If we trigger handlers at end of swipe OR, we trigger during, but they didnt trigger and we are still in the move phase
860
      if (didSwipeBackToCancel() || !validateSwipeDistance()) {
861
        phase = PHASE_CANCEL;
862
        triggerHandler(event, phase);
863
      } else if (options.triggerOnTouchEnd || (options.triggerOnTouchEnd === false && phase === PHASE_MOVE)) {
864
        //call this on jq event so we are cross browser
865
        if (options.preventDefaultEvents !== false && jqEvent.cancelable !== false) {
866
          jqEvent.preventDefault();
867
        }
868
        phase = PHASE_END;
869
        triggerHandler(event, phase);
870
      }
871
      //Special cases - A tap should always fire on touch end regardless,
872
      //So here we manually trigger the tap end handler by itself
873
      //We dont run trigger handler as it will re-trigger events that may have fired already
874
      else if (!options.triggerOnTouchEnd && hasTap()) {
875
        //Trigger the pinch events...
876
        phase = PHASE_END;
877
        triggerHandlerForGesture(event, phase, TAP);
878
      } else if (phase === PHASE_MOVE) {
879
        phase = PHASE_CANCEL;
880
        triggerHandler(event, phase);
881
      }
882
 
883
      setTouchInProgress(false);
884
 
885
      return null;
886
    }
887
 
888
 
889
 
890
    /**
891
     * Event handler for a touch cancel event.
892
     * Clears current vars
893
     * @inner
894
     */
895
    function touchCancel() {
896
      // reset the variables back to default values
897
      fingerCount = 0;
898
      endTime = 0;
899
      startTime = 0;
900
      startTouchesDistance = 0;
901
      endTouchesDistance = 0;
902
      pinchZoom = 1;
903
 
904
      //If we were in progress of tracking a possible multi touch end, then re set it.
905
      cancelMultiFingerRelease();
906
 
907
      setTouchInProgress(false);
908
    }
909
 
910
 
911
    /**
912
     * Event handler for a touch leave event.
913
     * This is only triggered on desktops, in touch we work this out manually
914
     * as the touchleave event is not supported in webkit
915
     * @inner
916
     */
917
    function touchLeave(jqEvent) {
918
      //If these events are being programmatically triggered, we don't have an original event object, so use the Jq one.
919
      var event = jqEvent.originalEvent ? jqEvent.originalEvent : jqEvent;
920
 
921
      //If we have the trigger on leave property set....
922
      if (options.triggerOnTouchLeave) {
923
        phase = getNextPhase(PHASE_END);
924
        triggerHandler(event, phase);
925
      }
926
    }
927
 
928
    /**
929
     * Removes all listeners that were associated with the plugin
930
     * @inner
931
     */
932
    function removeListeners() {
933
      $element.off(START_EV, touchStart);
934
      $element.off(CANCEL_EV, touchCancel);
935
      $element.off(MOVE_EV, touchMove);
936
      $element.off(END_EV, touchEnd);
937
 
938
      //we only have leave events on desktop, we manually calculate leave on touch as its not supported in webkit
939
      if (LEAVE_EV) {
940
        $element.off(LEAVE_EV, touchLeave);
941
      }
942
 
943
      setTouchInProgress(false);
944
    }
945
 
946
 
947
    /**
948
     * Checks if the time and distance thresholds have been met, and if so then the appropriate handlers are fired.
949
     */
950
    function getNextPhase(currentPhase) {
951
 
952
      var nextPhase = currentPhase;
953
 
954
      // Ensure we have valid swipe (under time and over distance  and check if we are out of bound...)
955
      var validTime = validateSwipeTime();
956
      var validDistance = validateSwipeDistance();
957
      var didCancel = didSwipeBackToCancel();
958
 
959
      //If we have exceeded our time, then cancel
960
      if (!validTime || didCancel) {
961
        nextPhase = PHASE_CANCEL;
962
      }
963
      //Else if we are moving, and have reached distance then end
964
      else if (validDistance && currentPhase == PHASE_MOVE && (!options.triggerOnTouchEnd || options.triggerOnTouchLeave)) {
965
        nextPhase = PHASE_END;
966
      }
967
      //Else if we have ended by leaving and didn't reach distance, then cancel
968
      else if (!validDistance && currentPhase == PHASE_END && options.triggerOnTouchLeave) {
969
        nextPhase = PHASE_CANCEL;
970
      }
971
 
972
      return nextPhase;
973
    }
974
 
975
 
976
    /**
977
     * Trigger the relevant event handler
978
     * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down"
979
     * @param {object} event the original event object
980
     * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases}
981
     * @inner
982
     */
983
    function triggerHandler(event, phase) {
984
 
985
 
986
 
987
      var ret,
988
        touches = event.touches;
989
 
990
      // SWIPE GESTURES
991
      if (didSwipe() || hasSwipes()) {
992
          ret = triggerHandlerForGesture(event, phase, SWIPE);
993
      }
994
 
995
      // PINCH GESTURES (if the above didn't cancel)
996
      if ((didPinch() || hasPinches()) && ret !== false) {
997
          ret = triggerHandlerForGesture(event, phase, PINCH);
998
      }
999
 
1000
      // CLICK / TAP (if the above didn't cancel)
1001
      if (didDoubleTap() && ret !== false) {
1002
        //Trigger the tap events...
1003
        ret = triggerHandlerForGesture(event, phase, DOUBLE_TAP);
1004
      }
1005
 
1006
      // CLICK / TAP (if the above didn't cancel)
1007
      else if (didLongTap() && ret !== false) {
1008
        //Trigger the tap events...
1009
        ret = triggerHandlerForGesture(event, phase, LONG_TAP);
1010
      }
1011
 
1012
      // CLICK / TAP (if the above didn't cancel)
1013
      else if (didTap() && ret !== false) {
1014
        //Trigger the tap event..
1015
        ret = triggerHandlerForGesture(event, phase, TAP);
1016
      }
1017
 
1018
 
1019
 
1020
      // If we are cancelling the gesture, then manually trigger the reset handler
1021
      if (phase === PHASE_CANCEL) {
1022
 
1023
        touchCancel(event);
1024
      }
1025
 
1026
 
1027
 
1028
 
1029
      // If we are ending the gesture, then manually trigger the reset handler IF all fingers are off
1030
      if (phase === PHASE_END) {
1031
        //If we support touch, then check that all fingers are off before we cancel
1032
        if (touches) {
1033
          if (!touches.length) {
1034
            touchCancel(event);
1035
          }
1036
        } else {
1037
          touchCancel(event);
1038
        }
1039
      }
1040
 
1041
      return ret;
1042
    }
1043
 
1044
 
1045
 
1046
    /**
1047
     * Trigger the relevant event handler
1048
     * The handlers are passed the original event, the element that was swiped, and in the case of the catch all handler, the direction that was swiped, "left", "right", "up", or "down"
1049
     * @param {object} event the original event object
1050
     * @param {string} phase the phase of the swipe (start, end cancel etc) {@link $.fn.swipe.phases}
1051
     * @param {string} gesture the gesture to trigger a handler for : PINCH or SWIPE {@link $.fn.swipe.gestures}
1052
     * @return Boolean False, to indicate that the event should stop propagation, or void.
1053
     * @inner
1054
     */
1055
    function triggerHandlerForGesture(event, phase, gesture) {
1056
 
1057
      var ret;
1058
 
1059
      //SWIPES....
1060
      if (gesture == SWIPE) {
1061
        //Trigger status every time..
1062
        $element.trigger('swipeStatus', [phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData, currentDirection]);
1063
 
1064
        if (options.swipeStatus) {
1065
          ret = options.swipeStatus.call($element, event, phase, direction || null, distance || 0, duration || 0, fingerCount, fingerData, currentDirection);
1066
          //If the status cancels, then dont run the subsequent event handlers..
1067
          if (ret === false) return false;
1068
        }
1069
 
1070
        if (phase == PHASE_END && validateSwipe()) {
1071
 
1072
          //Cancel any taps that were in progress...
1073
          clearTimeout(singleTapTimeout);
1074
          clearTimeout(holdTimeout);
1075
 
1076
          $element.trigger('swipe', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
1077
 
1078
          if (options.swipe) {
1079
            ret = options.swipe.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
1080
            //If the status cancels, then dont run the subsequent event handlers..
1081
            if (ret === false) return false;
1082
          }
1083
 
1084
          //trigger direction specific event handlers
1085
          switch (direction) {
1086
            case LEFT:
1087
              $element.trigger('swipeLeft', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
1088
 
1089
              if (options.swipeLeft) {
1090
                ret = options.swipeLeft.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
1091
              }
1092
              break;
1093
 
1094
            case RIGHT:
1095
              $element.trigger('swipeRight', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
1096
 
1097
              if (options.swipeRight) {
1098
                ret = options.swipeRight.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
1099
              }
1100
              break;
1101
 
1102
            case UP:
1103
              $element.trigger('swipeUp', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
1104
 
1105
              if (options.swipeUp) {
1106
                ret = options.swipeUp.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
1107
              }
1108
              break;
1109
 
1110
            case DOWN:
1111
              $element.trigger('swipeDown', [direction, distance, duration, fingerCount, fingerData, currentDirection]);
1112
 
1113
              if (options.swipeDown) {
1114
                ret = options.swipeDown.call($element, event, direction, distance, duration, fingerCount, fingerData, currentDirection);
1115
              }
1116
              break;
1117
          }
1118
        }
1119
      }
1120
 
1121
 
1122
      //PINCHES....
1123
      if (gesture == PINCH) {
1124
        $element.trigger('pinchStatus', [phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
1125
 
1126
        if (options.pinchStatus) {
1127
          ret = options.pinchStatus.call($element, event, phase, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
1128
          //If the status cancels, then dont run the subsequent event handlers..
1129
          if (ret === false) return false;
1130
        }
1131
 
1132
        if (phase == PHASE_END && validatePinch()) {
1133
 
1134
          switch (pinchDirection) {
1135
            case IN:
1136
              $element.trigger('pinchIn', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
1137
 
1138
              if (options.pinchIn) {
1139
                ret = options.pinchIn.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
1140
              }
1141
              break;
1142
 
1143
            case OUT:
1144
              $element.trigger('pinchOut', [pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData]);
1145
 
1146
              if (options.pinchOut) {
1147
                ret = options.pinchOut.call($element, event, pinchDirection || null, pinchDistance || 0, duration || 0, fingerCount, pinchZoom, fingerData);
1148
              }
1149
              break;
1150
          }
1151
        }
1152
      }
1153
 
1154
      if (gesture == TAP) {
1155
        if (phase === PHASE_CANCEL || phase === PHASE_END) {
1156
 
1157
          clearTimeout(singleTapTimeout);
1158
          clearTimeout(holdTimeout);
1159
 
1160
          //If we are also looking for doubelTaps, wait incase this is one...
1161
          if (hasDoubleTap() && !inDoubleTap()) {
1162
            doubleTapStartTime = getTimeStamp();
1163
 
1164
            //Now wait for the double tap timeout, and trigger this single tap
1165
            //if its not cancelled by a double tap
1166
            singleTapTimeout = setTimeout($.proxy(function() {
1167
              doubleTapStartTime = null;
1168
              $element.trigger('tap', [event.target]);
1169
 
1170
              if (options.tap) {
1171
                ret = options.tap.call($element, event, event.target);
1172
              }
1173
            }, this), options.doubleTapThreshold);
1174
 
1175
          } else {
1176
            doubleTapStartTime = null;
1177
            $element.trigger('tap', [event.target]);
1178
            if (options.tap) {
1179
              ret = options.tap.call($element, event, event.target);
1180
            }
1181
          }
1182
        }
1183
      } else if (gesture == DOUBLE_TAP) {
1184
        if (phase === PHASE_CANCEL || phase === PHASE_END) {
1185
          clearTimeout(singleTapTimeout);
1186
          clearTimeout(holdTimeout);
1187
          doubleTapStartTime = null;
1188
          $element.trigger('doubletap', [event.target]);
1189
 
1190
          if (options.doubleTap) {
1191
            ret = options.doubleTap.call($element, event, event.target);
1192
          }
1193
        }
1194
      } else if (gesture == LONG_TAP) {
1195
        if (phase === PHASE_CANCEL || phase === PHASE_END) {
1196
          clearTimeout(singleTapTimeout);
1197
          doubleTapStartTime = null;
1198
 
1199
          $element.trigger('longtap', [event.target]);
1200
          if (options.longTap) {
1201
            ret = options.longTap.call($element, event, event.target);
1202
          }
1203
        }
1204
      }
1205
 
1206
      return ret;
1207
    }
1208
 
1209
 
1210
    //
1211
    // GESTURE VALIDATION
1212
    //
1213
 
1214
    /**
1215
     * Checks the user has swipe far enough
1216
     * @return Boolean if <code>threshold</code> has been set, return true if the threshold was met, else false.
1217
     * If no threshold was set, then we return true.
1218
     * @inner
1219
     */
1220
    function validateSwipeDistance() {
1221
      var valid = true;
1222
      //If we made it past the min swipe distance..
1223
      if (options.threshold !== null) {
1224
        valid = distance >= options.threshold;
1225
      }
1226
 
1227
      return valid;
1228
    }
1229
 
1230
    /**
1231
     * Checks the user has swiped back to cancel.
1232
     * @return Boolean if <code>cancelThreshold</code> has been set, return true if the cancelThreshold was met, else false.
1233
     * If no cancelThreshold was set, then we return true.
1234
     * @inner
1235
     */
1236
    function didSwipeBackToCancel() {
1237
      var cancelled = false;
1238
      if (options.cancelThreshold !== null && direction !== null) {
1239
        cancelled = (getMaxDistance(direction) - distance) >= options.cancelThreshold;
1240
      }
1241
 
1242
      return cancelled;
1243
    }
1244
 
1245
    /**
1246
     * Checks the user has pinched far enough
1247
     * @return Boolean if <code>pinchThreshold</code> has been set, return true if the threshold was met, else false.
1248
     * If no threshold was set, then we return true.
1249
     * @inner
1250
     */
1251
    function validatePinchDistance() {
1252
      if (options.pinchThreshold !== null) {
1253
        return pinchDistance >= options.pinchThreshold;
1254
      }
1255
      return true;
1256
    }
1257
 
1258
    /**
1259
     * Checks that the time taken to swipe meets the minimum / maximum requirements
1260
     * @return Boolean
1261
     * @inner
1262
     */
1263
    function validateSwipeTime() {
1264
      var result;
1265
      //If no time set, then return true
1266
      if (options.maxTimeThreshold) {
1267
        if (duration >= options.maxTimeThreshold) {
1268
          result = false;
1269
        } else {
1270
          result = true;
1271
        }
1272
      } else {
1273
        result = true;
1274
      }
1275
 
1276
      return result;
1277
    }
1278
 
1279
 
1280
    /**
1281
     * Checks direction of the swipe and the value allowPageScroll to see if we should allow or prevent the default behaviour from occurring.
1282
     * This will essentially allow page scrolling or not when the user is swiping on a touchSwipe object.
1283
     * @param {object} jqEvent The normalised jQuery representation of the event object.
1284
     * @param {string} direction The direction of the event. See {@link $.fn.swipe.directions}
1285
     * @see $.fn.swipe.directions
1286
     * @inner
1287
     */
1288
    function validateDefaultEvent(jqEvent, direction) {
1289
 
1290
      //If the option is set, allways allow the event to bubble up (let user handle weirdness)
1291
      if (options.preventDefaultEvents === false) {
1292
        return;
1293
      }
1294
 
1295
      if (options.allowPageScroll === NONE) {
1296
        jqEvent.preventDefault();
1297
      } else {
1298
        var auto = options.allowPageScroll === AUTO;
1299
 
1300
        switch (direction) {
1301
          case LEFT:
1302
            if ((options.swipeLeft && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) {
1303
              jqEvent.preventDefault();
1304
            }
1305
            break;
1306
 
1307
          case RIGHT:
1308
            if ((options.swipeRight && auto) || (!auto && options.allowPageScroll != HORIZONTAL)) {
1309
              jqEvent.preventDefault();
1310
            }
1311
            break;
1312
 
1313
          case UP:
1314
            if ((options.swipeUp && auto) || (!auto && options.allowPageScroll != VERTICAL)) {
1315
              jqEvent.preventDefault();
1316
            }
1317
            break;
1318
 
1319
          case DOWN:
1320
            if ((options.swipeDown && auto) || (!auto && options.allowPageScroll != VERTICAL)) {
1321
              jqEvent.preventDefault();
1322
            }
1323
            break;
1324
 
1325
          case NONE:
1326
 
1327
            break;
1328
        }
1329
      }
1330
    }
1331
 
1332
 
1333
    // PINCHES
1334
    /**
1335
     * Returns true of the current pinch meets the thresholds
1336
     * @return Boolean
1337
     * @inner
1338
     */
1339
    function validatePinch() {
1340
      var hasCorrectFingerCount = validateFingers();
1341
      var hasEndPoint = validateEndPoint();
1342
      var hasCorrectDistance = validatePinchDistance();
1343
      return hasCorrectFingerCount && hasEndPoint && hasCorrectDistance;
1344
 
1345
    }
1346
 
1347
    /**
1348
     * Returns true if any Pinch events have been registered
1349
     * @return Boolean
1350
     * @inner
1351
     */
1352
    function hasPinches() {
1353
      //Enure we dont return 0 or null for false values
1354
      return !!(options.pinchStatus || options.pinchIn || options.pinchOut);
1355
    }
1356
 
1357
    /**
1358
     * Returns true if we are detecting pinches, and have one
1359
     * @return Boolean
1360
     * @inner
1361
     */
1362
    function didPinch() {
1363
      //Enure we dont return 0 or null for false values
1364
      return !!(validatePinch() && hasPinches());
1365
    }
1366
 
1367
 
1368
 
1369
 
1370
    // SWIPES
1371
    /**
1372
     * Returns true if the current swipe meets the thresholds
1373
     * @return Boolean
1374
     * @inner
1375
     */
1376
    function validateSwipe() {
1377
      //Check validity of swipe
1378
      var hasValidTime = validateSwipeTime();
1379
      var hasValidDistance = validateSwipeDistance();
1380
      var hasCorrectFingerCount = validateFingers();
1381
      var hasEndPoint = validateEndPoint();
1382
      var didCancel = didSwipeBackToCancel();
1383
 
1384
      // if the user swiped more than the minimum length, perform the appropriate action
1385
      // hasValidDistance is null when no distance is set
1386
      var valid = !didCancel && hasEndPoint && hasCorrectFingerCount && hasValidDistance && hasValidTime;
1387
 
1388
      return valid;
1389
    }
1390
 
1391
    /**
1392
     * Returns true if any Swipe events have been registered
1393
     * @return Boolean
1394
     * @inner
1395
     */
1396
    function hasSwipes() {
1397
      //Enure we dont return 0 or null for false values
1398
      return !!(options.swipe || options.swipeStatus || options.swipeLeft || options.swipeRight || options.swipeUp || options.swipeDown);
1399
    }
1400
 
1401
 
1402
    /**
1403
     * Returns true if we are detecting swipes and have one
1404
     * @return Boolean
1405
     * @inner
1406
     */
1407
    function didSwipe() {
1408
      //Enure we dont return 0 or null for false values
1409
      return !!(validateSwipe() && hasSwipes());
1410
    }
1411
 
1412
    /**
1413
     * Returns true if we have matched the number of fingers we are looking for
1414
     * @return Boolean
1415
     * @inner
1416
     */
1417
    function validateFingers() {
1418
      //The number of fingers we want were matched, or on desktop we ignore
1419
      return ((fingerCount === options.fingers || options.fingers === ALL_FINGERS) || !SUPPORTS_TOUCH);
1420
    }
1421
 
1422
    /**
1423
     * Returns true if we have an end point for the swipe
1424
     * @return Boolean
1425
     * @inner
1426
     */
1427
    function validateEndPoint() {
1428
      //We have an end value for the finger
1429
      return fingerData[0].end.x !== 0;
1430
    }
1431
 
1432
    // TAP / CLICK
1433
    /**
1434
     * Returns true if a click / tap events have been registered
1435
     * @return Boolean
1436
     * @inner
1437
     */
1438
    function hasTap() {
1439
      //Enure we dont return 0 or null for false values
1440
      return !!(options.tap);
1441
    }
1442
 
1443
    /**
1444
     * Returns true if a double tap events have been registered
1445
     * @return Boolean
1446
     * @inner
1447
     */
1448
    function hasDoubleTap() {
1449
      //Enure we dont return 0 or null for false values
1450
      return !!(options.doubleTap);
1451
    }
1452
 
1453
    /**
1454
     * Returns true if any long tap events have been registered
1455
     * @return Boolean
1456
     * @inner
1457
     */
1458
    function hasLongTap() {
1459
      //Enure we dont return 0 or null for false values
1460
      return !!(options.longTap);
1461
    }
1462
 
1463
    /**
1464
     * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past.
1465
     * @return Boolean
1466
     * @inner
1467
     */
1468
    function validateDoubleTap() {
1469
      if (doubleTapStartTime == null) {
1470
        return false;
1471
      }
1472
      var now = getTimeStamp();
1473
      return (hasDoubleTap() && ((now - doubleTapStartTime) <= options.doubleTapThreshold));
1474
    }
1475
 
1476
    /**
1477
     * Returns true if we could be in the process of a double tap (one tap has occurred, we are listening for double taps, and the threshold hasn't past.
1478
     * @return Boolean
1479
     * @inner
1480
     */
1481
    function inDoubleTap() {
1482
      return validateDoubleTap();
1483
    }
1484
 
1485
 
1486
    /**
1487
     * Returns true if we have a valid tap
1488
     * @return Boolean
1489
     * @inner
1490
     */
1491
    function validateTap() {
1492
      return ((fingerCount === 1 || !SUPPORTS_TOUCH) && (isNaN(distance) || distance < options.threshold));
1493
    }
1494
 
1495
    /**
1496
     * Returns true if we have a valid long tap
1497
     * @return Boolean
1498
     * @inner
1499
     */
1500
    function validateLongTap() {
1501
      //slight threshold on moving finger
1502
      return ((duration > options.longTapThreshold) && (distance < DOUBLE_TAP_THRESHOLD));
1503
    }
1504
 
1505
    /**
1506
     * Returns true if we are detecting taps and have one
1507
     * @return Boolean
1508
     * @inner
1509
     */
1510
    function didTap() {
1511
      //Enure we dont return 0 or null for false values
1512
      return !!(validateTap() && hasTap());
1513
    }
1514
 
1515
 
1516
    /**
1517
     * Returns true if we are detecting double taps and have one
1518
     * @return Boolean
1519
     * @inner
1520
     */
1521
    function didDoubleTap() {
1522
      //Enure we dont return 0 or null for false values
1523
      return !!(validateDoubleTap() && hasDoubleTap());
1524
    }
1525
 
1526
    /**
1527
     * Returns true if we are detecting long taps and have one
1528
     * @return Boolean
1529
     * @inner
1530
     */
1531
    function didLongTap() {
1532
      //Enure we dont return 0 or null for false values
1533
      return !!(validateLongTap() && hasLongTap());
1534
    }
1535
 
1536
 
1537
 
1538
 
1539
    // MULTI FINGER TOUCH
1540
    /**
1541
     * Starts tracking the time between 2 finger releases, and keeps track of how many fingers we initially had up
1542
     * @inner
1543
     */
1544
    function startMultiFingerRelease(event) {
1545
      previousTouchEndTime = getTimeStamp();
1546
      fingerCountAtRelease = event.touches.length + 1;
1547
    }
1548
 
1549
    /**
1550
     * Cancels the tracking of time between 2 finger releases, and resets counters
1551
     * @inner
1552
     */
1553
    function cancelMultiFingerRelease() {
1554
      previousTouchEndTime = 0;
1555
      fingerCountAtRelease = 0;
1556
    }
1557
 
1558
    /**
1559
     * Checks if we are in the threshold between 2 fingers being released
1560
     * @return Boolean
1561
     * @inner
1562
     */
1563
    function inMultiFingerRelease() {
1564
 
1565
      var withinThreshold = false;
1566
 
1567
      if (previousTouchEndTime) {
1568
        var diff = getTimeStamp() - previousTouchEndTime
1569
        if (diff <= options.fingerReleaseThreshold) {
1570
          withinThreshold = true;
1571
        }
1572
      }
1573
 
1574
      return withinThreshold;
1575
    }
1576
 
1577
 
1578
    /**
1579
     * gets a data flag to indicate that a touch is in progress
1580
     * @return Boolean
1581
     * @inner
1582
     */
1583
    function getTouchInProgress() {
1584
      //strict equality to ensure only true and false are returned
1585
      return !!($element.data(PLUGIN_NS + '_intouch') === true);
1586
    }
1587
 
1588
    /**
1589
     * Sets a data flag to indicate that a touch is in progress
1590
     * @param {boolean} val The value to set the property to
1591
     * @inner
1592
     */
1593
    function setTouchInProgress(val) {
1594
 
1595
      //If destroy is called in an event handler, we have no el, and we have already cleaned up, so return.
1596
      if(!$element) { return; }
1597
 
1598
      //Add or remove event listeners depending on touch status
1599
      if (val === true) {
1600
        $element.on(MOVE_EV, touchMove);
1601
        $element.on(END_EV, touchEnd);
1602
 
1603
        //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit
1604
        if (LEAVE_EV) {
1605
          $element.on(LEAVE_EV, touchLeave);
1606
        }
1607
      } else {
1608
 
1609
        $element.off(MOVE_EV, touchMove, false);
1610
        $element.off(END_EV, touchEnd, false);
1611
 
1612
        //we only have leave events on desktop, we manually calcuate leave on touch as its not supported in webkit
1613
        if (LEAVE_EV) {
1614
          $element.off(LEAVE_EV, touchLeave, false);
1615
        }
1616
      }
1617
 
1618
 
1619
      //strict equality to ensure only true and false can update the value
1620
      $element.data(PLUGIN_NS + '_intouch', val === true);
1621
    }
1622
 
1623
 
1624
    /**
1625
     * Creates the finger data for the touch/finger in the event object.
1626
     * @param {int} id The id to store the finger data under (usually the order the fingers were pressed)
1627
     * @param {object} evt The event object containing finger data
1628
     * @return finger data object
1629
     * @inner
1630
     */
1631
    function createFingerData(id, evt) {
1632
      var f = {
1633
        start: {
1634
          x: 0,
1635
          y: 0
1636
        },
1637
        last: {
1638
          x: 0,
1639
          y: 0
1640
        },
1641
        end: {
1642
          x: 0,
1643
          y: 0
1644
        }
1645
      };
1646
      f.start.x = f.last.x = f.end.x = evt.pageX || evt.clientX;
1647
      f.start.y = f.last.y = f.end.y = evt.pageY || evt.clientY;
1648
      fingerData[id] = f;
1649
      return f;
1650
    }
1651
 
1652
    /**
1653
     * Updates the finger data for a particular event object
1654
     * @param {object} evt The event object containing the touch/finger data to upadte
1655
     * @return a finger data object.
1656
     * @inner
1657
     */
1658
    function updateFingerData(evt) {
1659
      var id = evt.identifier !== undefined ? evt.identifier : 0;
1660
      var f = getFingerData(id);
1661
 
1662
      if (f === null) {
1663
        f = createFingerData(id, evt);
1664
      }
1665
 
1666
      f.last.x = f.end.x;
1667
      f.last.y = f.end.y;
1668
 
1669
      f.end.x = evt.pageX || evt.clientX;
1670
      f.end.y = evt.pageY || evt.clientY;
1671
 
1672
      return f;
1673
    }
1674
 
1675
    /**
1676
     * Returns a finger data object by its event ID.
1677
     * Each touch event has an identifier property, which is used
1678
     * to track repeat touches
1679
     * @param {int} id The unique id of the finger in the sequence of touch events.
1680
     * @return a finger data object.
1681
     * @inner
1682
     */
1683
    function getFingerData(id) {
1684
      return fingerData[id] || null;
1685
    }
1686
 
1687
 
1688
    /**
1689
     * Sets the maximum distance swiped in the given direction.
1690
     * If the new value is lower than the current value, the max value is not changed.
1691
     * @param {string}  direction The direction of the swipe
1692
     * @param {int}  distance The distance of the swipe
1693
     * @inner
1694
     */
1695
    function setMaxDistance(direction, distance) {
1696
      if(direction==NONE) return;
1697
      distance = Math.max(distance, getMaxDistance(direction));
1698
      maximumsMap[direction].distance = distance;
1699
    }
1700
 
1701
    /**
1702
     * gets the maximum distance swiped in the given direction.
1703
     * @param {string}  direction The direction of the swipe
1704
     * @return int  The distance of the swipe
1705
     * @inner
1706
     */
1707
    function getMaxDistance(direction) {
1708
      if (maximumsMap[direction]) return maximumsMap[direction].distance;
1709
      return undefined;
1710
    }
1711
 
1712
    /**
1713
     * Creats a map of directions to maximum swiped values.
1714
     * @return Object A dictionary of maximum values, indexed by direction.
1715
     * @inner
1716
     */
1717
    function createMaximumsData() {
1718
      var maxData = {};
1719
      maxData[LEFT] = createMaximumVO(LEFT);
1720
      maxData[RIGHT] = createMaximumVO(RIGHT);
1721
      maxData[UP] = createMaximumVO(UP);
1722
      maxData[DOWN] = createMaximumVO(DOWN);
1723
 
1724
      return maxData;
1725
    }
1726
 
1727
    /**
1728
     * Creates a map maximum swiped values for a given swipe direction
1729
     * @param {string} The direction that these values will be associated with
1730
     * @return Object Maximum values
1731
     * @inner
1732
     */
1733
    function createMaximumVO(dir) {
1734
      return {
1735
        direction: dir,
1736
        distance: 0
1737
      }
1738
    }
1739
 
1740
 
1741
    //
1742
    // MATHS / UTILS
1743
    //
1744
 
1745
    /**
1746
     * Calculate the duration of the swipe
1747
     * @return int
1748
     * @inner
1749
     */
1750
    function calculateDuration() {
1751
      return endTime - startTime;
1752
    }
1753
 
1754
    /**
1755
     * Calculate the distance between 2 touches (pinch)
1756
     * @param {point} startPoint A point object containing x and y co-ordinates
1757
     * @param {point} endPoint A point object containing x and y co-ordinates
1758
     * @return int;
1759
     * @inner
1760
     */
1761
    function calculateTouchesDistance(startPoint, endPoint) {
1762
      var diffX = Math.abs(startPoint.x - endPoint.x);
1763
      var diffY = Math.abs(startPoint.y - endPoint.y);
1764
 
1765
      return Math.round(Math.sqrt(diffX * diffX + diffY * diffY));
1766
    }
1767
 
1768
    /**
1769
     * Calculate the zoom factor between the start and end distances
1770
     * @param {int} startDistance Distance (between 2 fingers) the user started pinching at
1771
     * @param {int} endDistance Distance (between 2 fingers) the user ended pinching at
1772
     * @return float The zoom value from 0 to 1.
1773
     * @inner
1774
     */
1775
    function calculatePinchZoom(startDistance, endDistance) {
1776
      var percent = (endDistance / startDistance) * 1;
1777
      return percent.toFixed(2);
1778
    }
1779
 
1780
 
1781
    /**
1782
     * Returns the pinch direction, either IN or OUT for the given points
1783
     * @return string Either {@link $.fn.swipe.directions.IN} or {@link $.fn.swipe.directions.OUT}
1784
     * @see $.fn.swipe.directions
1785
     * @inner
1786
     */
1787
    function calculatePinchDirection() {
1788
      if (pinchZoom < 1) {
1789
        return OUT;
1790
      } else {
1791
        return IN;
1792
      }
1793
    }
1794
 
1795
 
1796
    /**
1797
     * Calculate the length / distance of the swipe
1798
     * @param {point} startPoint A point object containing x and y co-ordinates
1799
     * @param {point} endPoint A point object containing x and y co-ordinates
1800
     * @return int
1801
     * @inner
1802
     */
1803
    function calculateDistance(startPoint, endPoint) {
1804
      return Math.round(Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2)));
1805
    }
1806
 
1807
    /**
1808
     * Calculate the angle of the swipe
1809
     * @param {point} startPoint A point object containing x and y co-ordinates
1810
     * @param {point} endPoint A point object containing x and y co-ordinates
1811
     * @return int
1812
     * @inner
1813
     */
1814
    function calculateAngle(startPoint, endPoint) {
1815
      var x = startPoint.x - endPoint.x;
1816
      var y = endPoint.y - startPoint.y;
1817
      var r = Math.atan2(y, x); //radians
1818
      var angle = Math.round(r * 180 / Math.PI); //degrees
1819
 
1820
      //ensure value is positive
1821
      if (angle < 0) {
1822
        angle = 360 - Math.abs(angle);
1823
      }
1824
 
1825
      return angle;
1826
    }
1827
 
1828
    /**
1829
     * Calculate the direction of the swipe
1830
     * This will also call calculateAngle to get the latest angle of swipe
1831
     * @param {point} startPoint A point object containing x and y co-ordinates
1832
     * @param {point} endPoint A point object containing x and y co-ordinates
1833
     * @return string Either {@link $.fn.swipe.directions.LEFT} / {@link $.fn.swipe.directions.RIGHT} / {@link $.fn.swipe.directions.DOWN} / {@link $.fn.swipe.directions.UP}
1834
     * @see $.fn.swipe.directions
1835
     * @inner
1836
     */
1837
    function calculateDirection(startPoint, endPoint) {
1838
 
1839
      if( comparePoints(startPoint, endPoint) ) {
1840
        return NONE;
1841
      }
1842
 
1843
      var angle = calculateAngle(startPoint, endPoint);
1844
 
1845
      if ((angle <= 45) && (angle >= 0)) {
1846
        return LEFT;
1847
      } else if ((angle <= 360) && (angle >= 315)) {
1848
        return LEFT;
1849
      } else if ((angle >= 135) && (angle <= 225)) {
1850
        return RIGHT;
1851
      } else if ((angle > 45) && (angle < 135)) {
1852
        return DOWN;
1853
      } else {
1854
        return UP;
1855
      }
1856
    }
1857
 
1858
 
1859
    /**
1860
     * Returns a MS time stamp of the current time
1861
     * @return int
1862
     * @inner
1863
     */
1864
    function getTimeStamp() {
1865
      var now = new Date();
1866
      return now.getTime();
1867
    }
1868
 
1869
 
1870
 
1871
    /**
1872
     * Returns a bounds object with left, right, top and bottom properties for the element specified.
1873
     * @param {DomNode} The DOM node to get the bounds for.
1874
     */
1875
    function getbounds(el) {
1876
      el = $(el);
1877
      var offset = el.offset();
1878
 
1879
      var bounds = {
1880
        left: offset.left,
1881
        right: offset.left + el.outerWidth(),
1882
        top: offset.top,
1883
        bottom: offset.top + el.outerHeight()
1884
      }
1885
 
1886
      return bounds;
1887
    }
1888
 
1889
 
1890
    /**
1891
     * Checks if the point object is in the bounds object.
1892
     * @param {object} point A point object.
1893
     * @param {int} point.x The x value of the point.
1894
     * @param {int} point.y The x value of the point.
1895
     * @param {object} bounds The bounds object to test
1896
     * @param {int} bounds.left The leftmost value
1897
     * @param {int} bounds.right The righttmost value
1898
     * @param {int} bounds.top The topmost value
1899
     * @param {int} bounds.bottom The bottommost value
1900
     */
1901
    function isInBounds(point, bounds) {
1902
      return (point.x > bounds.left && point.x < bounds.right && point.y > bounds.top && point.y < bounds.bottom);
1903
    };
1904
 
1905
    /**
1906
     * Checks if the two points are equal
1907
     * @param {object} point A point object.
1908
     * @param {object} point B point object.
1909
     * @return true of the points match
1910
     */
1911
    function comparePoints(pointA, pointB) {
1912
      return (pointA.x == pointB.x && pointA.y == pointB.y);
1913
    }
1914
 
1915
 
1916
  }
1917
 
1918
 
1919
 
1920
 
1921
  /**
1922
   * A catch all handler that is triggered for all swipe directions.
1923
   * @name $.fn.swipe#swipe
1924
   * @event
1925
   * @default null
1926
   * @param {EventObject} event The original event object
1927
   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
1928
   * @param {int} distance The distance the user swiped
1929
   * @param {int} duration The duration of the swipe in milliseconds
1930
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
1931
   * @param {object} fingerData The coordinates of fingers in event
1932
   * @param {string} currentDirection The current direction the user is swiping.
1933
   */
1934
 
1935
 
1936
 
1937
 
1938
  /**
1939
   * A handler that is triggered for "left" swipes.
1940
   * @name $.fn.swipe#swipeLeft
1941
   * @event
1942
   * @default null
1943
   * @param {EventObject} event The original event object
1944
   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
1945
   * @param {int} distance The distance the user swiped
1946
   * @param {int} duration The duration of the swipe in milliseconds
1947
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
1948
   * @param {object} fingerData The coordinates of fingers in event
1949
   * @param {string} currentDirection The current direction the user is swiping.
1950
   */
1951
 
1952
  /**
1953
   * A handler that is triggered for "right" swipes.
1954
   * @name $.fn.swipe#swipeRight
1955
   * @event
1956
   * @default null
1957
   * @param {EventObject} event The original event object
1958
   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
1959
   * @param {int} distance The distance the user swiped
1960
   * @param {int} duration The duration of the swipe in milliseconds
1961
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
1962
   * @param {object} fingerData The coordinates of fingers in event
1963
   * @param {string} currentDirection The current direction the user is swiping.
1964
   */
1965
 
1966
  /**
1967
   * A handler that is triggered for "up" swipes.
1968
   * @name $.fn.swipe#swipeUp
1969
   * @event
1970
   * @default null
1971
   * @param {EventObject} event The original event object
1972
   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
1973
   * @param {int} distance The distance the user swiped
1974
   * @param {int} duration The duration of the swipe in milliseconds
1975
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
1976
   * @param {object} fingerData The coordinates of fingers in event
1977
   * @param {string} currentDirection The current direction the user is swiping.
1978
   */
1979
 
1980
  /**
1981
   * A handler that is triggered for "down" swipes.
1982
   * @name $.fn.swipe#swipeDown
1983
   * @event
1984
   * @default null
1985
   * @param {EventObject} event The original event object
1986
   * @param {int} direction The direction the user swiped in. See {@link $.fn.swipe.directions}
1987
   * @param {int} distance The distance the user swiped
1988
   * @param {int} duration The duration of the swipe in milliseconds
1989
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
1990
   * @param {object} fingerData The coordinates of fingers in event
1991
   * @param {string} currentDirection The current direction the user is swiping.
1992
   */
1993
 
1994
  /**
1995
   * A handler triggered for every phase of the swipe. This handler is constantly fired for the duration of the pinch.
1996
   * This is triggered regardless of swipe thresholds.
1997
   * @name $.fn.swipe#swipeStatus
1998
   * @event
1999
   * @default null
2000
   * @param {EventObject} event The original event object
2001
   * @param {string} phase The phase of the swipe event. See {@link $.fn.swipe.phases}
2002
   * @param {string} direction The direction the user swiped in. This is null if the user has yet to move. See {@link $.fn.swipe.directions}
2003
   * @param {int} distance The distance the user swiped. This is 0 if the user has yet to move.
2004
   * @param {int} duration The duration of the swipe in milliseconds
2005
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
2006
   * @param {object} fingerData The coordinates of fingers in event
2007
   * @param {string} currentDirection The current direction the user is swiping.
2008
   */
2009
 
2010
  /**
2011
   * A handler triggered for pinch in events.
2012
   * @name $.fn.swipe#pinchIn
2013
   * @event
2014
   * @default null
2015
   * @param {EventObject} event The original event object
2016
   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
2017
   * @param {int} distance The distance the user pinched
2018
   * @param {int} duration The duration of the swipe in milliseconds
2019
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
2020
   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
2021
   * @param {object} fingerData The coordinates of fingers in event
2022
   */
2023
 
2024
  /**
2025
   * A handler triggered for pinch out events.
2026
   * @name $.fn.swipe#pinchOut
2027
   * @event
2028
   * @default null
2029
   * @param {EventObject} event The original event object
2030
   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
2031
   * @param {int} distance The distance the user pinched
2032
   * @param {int} duration The duration of the swipe in milliseconds
2033
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
2034
   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
2035
   * @param {object} fingerData The coordinates of fingers in event
2036
   */
2037
 
2038
  /**
2039
   * A handler triggered for all pinch events. This handler is constantly fired for the duration of the pinch. This is triggered regardless of thresholds.
2040
   * @name $.fn.swipe#pinchStatus
2041
   * @event
2042
   * @default null
2043
   * @param {EventObject} event The original event object
2044
   * @param {int} direction The direction the user pinched in. See {@link $.fn.swipe.directions}
2045
   * @param {int} distance The distance the user pinched
2046
   * @param {int} duration The duration of the swipe in milliseconds
2047
   * @param {int} fingerCount The number of fingers used. See {@link $.fn.swipe.fingers}
2048
   * @param {int} zoom The zoom/scale level the user pinched too, 0-1.
2049
   * @param {object} fingerData The coordinates of fingers in event
2050
   */
2051
 
2052
  /**
2053
   * A click handler triggered when a user simply clicks, rather than swipes on an element.
2054
   * This is deprecated since version 1.6.2, any assignment to click will be assigned to the tap handler.
2055
   * You cannot use <code>on</code> to bind to this event as the default jQ <code>click</code> event will be triggered.
2056
   * Use the <code>tap</code> event instead.
2057
   * @name $.fn.swipe#click
2058
   * @event
2059
   * @deprecated since version 1.6.2, please use {@link $.fn.swipe#tap} instead
2060
   * @default null
2061
   * @param {EventObject} event The original event object
2062
   * @param {DomObject} target The element clicked on.
2063
   */
2064
 
2065
  /**
2066
   * A click / tap handler triggered when a user simply clicks or taps, rather than swipes on an element.
2067
   * @name $.fn.swipe#tap
2068
   * @event
2069
   * @default null
2070
   * @param {EventObject} event The original event object
2071
   * @param {DomObject} target The element clicked on.
2072
   */
2073
 
2074
  /**
2075
   * A double tap handler triggered when a user double clicks or taps on an element.
2076
   * You can set the time delay for a double tap with the {@link $.fn.swipe.defaults#doubleTapThreshold} property.
2077
   * Note: If you set both <code>doubleTap</code> and <code>tap</code> handlers, the <code>tap</code> event will be delayed by the <code>doubleTapThreshold</code>
2078
   * as the script needs to check if its a double tap.
2079
   * @name $.fn.swipe#doubleTap
2080
   * @see  $.fn.swipe.defaults#doubleTapThreshold
2081
   * @event
2082
   * @default null
2083
   * @param {EventObject} event The original event object
2084
   * @param {DomObject} target The element clicked on.
2085
   */
2086
 
2087
  /**
2088
   * A long tap handler triggered once a tap has been release if the tap was longer than the longTapThreshold.
2089
   * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property.
2090
   * @name $.fn.swipe#longTap
2091
   * @see  $.fn.swipe.defaults#longTapThreshold
2092
   * @event
2093
   * @default null
2094
   * @param {EventObject} event The original event object
2095
   * @param {DomObject} target The element clicked on.
2096
   */
2097
 
2098
  /**
2099
   * A hold tap handler triggered as soon as the longTapThreshold is reached
2100
   * You can set the time delay for a long tap with the {@link $.fn.swipe.defaults#longTapThreshold} property.
2101
   * @name $.fn.swipe#hold
2102
   * @see  $.fn.swipe.defaults#longTapThreshold
2103
   * @event
2104
   * @default null
2105
   * @param {EventObject} event The original event object
2106
   * @param {DomObject} target The element clicked on.
2107
   */
2108
 
2109
}));