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