Blame Raw
· · · 249 lines (7.8 KB)
0 contributors
1/*!
2 * jQuery UI Touch Punch 1.0.9 as modified by RWAP Software
3 * based on original touchpunch v0.2.3 which has not been updated since 2014
4 *
5 * Updates by RWAP Software to take account of various suggested changes on the original code issues
6 *
7 * Original: https://github.com/furf/jquery-ui-touch-punch
8 * Copyright 2011–2014, Dave Furfero
9 * Dual licensed under the MIT or GPL Version 2 licenses.
10 *
11 * Fork: https://github.com/RWAP/jquery-ui-touch-punch
12 *
13 * Depends:
14 * jquery.ui.widget.js
15 * jquery.ui.mouse.js
16 */
17
18(function( factory ) {
19 if ( typeof define === "function" && define.amd ) {
20
21 // AMD. Register as an anonymous module.
22 define([ "jquery", "jquery-ui" ], factory );
23 } else {
24
25 // Browser globals
26 factory( jQuery );
27 }
28}(function ($) {
29
30 // Detect touch support - Windows Surface devices and other touch devices
31 $.mspointer = window.navigator.msPointerEnabled;
32 $.touch = ( 'ontouchstart' in document
33 || 'ontouchstart' in window
34 || window.TouchEvent
35 || (window.DocumentTouch && document instanceof DocumentTouch)
36 || navigator.maxTouchPoints > 0
37 || navigator.msMaxTouchPoints > 0
38 );
39
40 // Ignore browsers without touch or mouse support
41 if ((!$.touch && !$.mspointer) || !$.ui.mouse) {
42 return;
43 }
44
45 let mouseProto = $.ui.mouse.prototype,
46 _mouseInit = mouseProto._mouseInit,
47 _mouseDestroy = mouseProto._mouseDestroy,
48 touchHandled;
49
50 /**
51 * Get the x,y position of a touch event
52 * @param {Object} event A touch event
53 */
54 function getTouchCoords (event) {
55 return {
56 x: event.originalEvent.changedTouches[0].pageX,
57 y: event.originalEvent.changedTouches[0].pageY
58 };
59 }
60
61 /**
62 * Simulate a mouse event based on a corresponding touch event
63 * @param {Object} event A touch event
64 * @param {String} simulatedType The corresponding mouse event
65 */
66 function simulateMouseEvent (event, simulatedType) {
67
68 // Ignore multi-touch events
69 if (event.originalEvent.touches.length > 1) {
70 return;
71 }
72
73 //Ignore input or textarea elements so user can still enter text
74 if ($(event.target).is("input") || $(event.target).is("textarea")) {
75 return;
76 }
77
78 // Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
79 if (event.cancelable) {
80 event.preventDefault();
81 }
82
83 let touch = event.originalEvent.changedTouches[0],
84 simulatedEvent = document.createEvent('MouseEvents');
85
86 // Initialize the simulated mouse event using the touch event's coordinates
87 simulatedEvent.initMouseEvent(
88 simulatedType, // type
89 true, // bubbles
90 true, // cancelable
91 window, // view
92 1, // detail
93 touch.screenX, // screenX
94 touch.screenY, // screenY
95 touch.clientX, // clientX
96 touch.clientY, // clientY
97 false, // ctrlKey
98 false, // altKey
99 false, // shiftKey
100 false, // metaKey
101 0, // button
102 null // relatedTarget
103 );
104
105 // Dispatch the simulated event to the target element
106 event.target.dispatchEvent(simulatedEvent);
107 }
108
109 /**
110 * Handle the jQuery UI widget's touchstart events
111 * @param {Object} event The widget element's touchstart event
112 */
113 mouseProto._touchStart = function (event) {
114
115 let self = this;
116
117 // Interaction time
118 this._startedMove = event.timeStamp;
119
120 // Track movement to determine if interaction was a click
121 self._startPos = getTouchCoords(event);
122
123 // Ignore the event if another widget is already being handled
124 if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
125 return;
126 }
127
128 // Set the flag to prevent other widgets from inheriting the touch event
129 touchHandled = true;
130
131 // Track movement to determine if interaction was a click
132 self._touchMoved = false;
133
134 // Simulate the mouseover event
135 simulateMouseEvent(event, 'mouseover');
136
137 // Simulate the mousemove event
138 simulateMouseEvent(event, 'mousemove');
139
140 // Simulate the mousedown event
141 simulateMouseEvent(event, 'mousedown');
142 };
143
144 /**
145 * Handle the jQuery UI widget's touchmove events
146 * @param {Object} event The document's touchmove event
147 */
148 mouseProto._touchMove = function (event) {
149
150 // Ignore event if not handled
151 if (!touchHandled) {
152 return;
153 }
154
155 // Interaction was moved
156 this._touchMoved = true;
157
158 // Simulate the mousemove event
159 simulateMouseEvent(event, 'mousemove');
160 };
161
162 /**
163 * Handle the jQuery UI widget's touchend events
164 * @param {Object} event The document's touchend event
165 */
166 mouseProto._touchEnd = function (event) {
167
168 // Ignore event if not handled
169 if (!touchHandled) {
170 return;
171 }
172
173 // Simulate the mouseup event
174 simulateMouseEvent(event, 'mouseup');
175
176 // Simulate the mouseout event
177 simulateMouseEvent(event, 'mouseout');
178
179 // If the touch interaction did not move, it should trigger a click
180 // Check for this in two ways - length of time of simulation and distance moved
181 // Allow for Apple Stylus to be used also
182 let timeMoving = event.timeStamp - this._startedMove;
183 if (!this._touchMoved || timeMoving < 500) {
184 // Simulate the click event
185 simulateMouseEvent(event, 'click');
186 } else {
187 let endPos = getTouchCoords(event);
188 if ((Math.abs(endPos.x - this._startPos.x) < 10) && (Math.abs(endPos.y - this._startPos.y) < 10)) {
189
190 // If the touch interaction did not move, it should trigger a click
191 if (!this._touchMoved || event.originalEvent.changedTouches[0].touchType === 'stylus') {
192 // Simulate the click event
193 simulateMouseEvent(event, 'click');
194 }
195 }
196 }
197
198 // Unset the flag to determine the touch movement stopped
199 this._touchMoved = false;
200
201 // Unset the flag to allow other widgets to inherit the touch event
202 touchHandled = false;
203 };
204
205 /**
206 * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
207 * This method extends the widget with bound touch event handlers that
208 * translate touch events to mouse events and pass them to the widget's
209 * original mouse event handling methods.
210 */
211 mouseProto._mouseInit = function () {
212
213 let self = this;
214
215 // Microsoft Surface Support = remove original touch Action
216 if ($.support.mspointer) {
217 self.element[0].style.msTouchAction = 'none';
218 }
219
220 // Delegate the touch handlers to the widget's element
221 self.element.on({
222 touchstart: $.proxy(self, '_touchStart'),
223 touchmove: $.proxy(self, '_touchMove'),
224 touchend: $.proxy(self, '_touchEnd')
225 });
226
227 // Call the original $.ui.mouse init method
228 _mouseInit.call(self);
229 };
230
231 /**
232 * Remove the touch event handlers
233 */
234 mouseProto._mouseDestroy = function () {
235
236 let self = this;
237
238 // Delegate the touch handlers to the widget's element
239 self.element.off({
240 touchstart: $.proxy(self, '_touchStart'),
241 touchmove: $.proxy(self, '_touchMove'),
242 touchend: $.proxy(self, '_touchEnd')
243 });
244
245 // Call the original $.ui.mouse destroy method
246 _mouseDestroy.call(self);
247 };
248
249}));