PageSourceSearch

https://yamahamotorsports.com/js/jquery.touchswipe.js

js yamahamotorsports.com collected 2026-09-24 17:41:34 UTC 72,750 bytes, 1,983 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.