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.