From 428e7b96fa29f7533b2c6cdecd3fdfb3ac22e162 Mon Sep 17 00:00:00 2001 From: Rand Scullard Date: Thu, 18 Dec 2014 16:54:59 -0500 Subject: [PATCH 1/5] Fixed issue #41: Scroll inside draggable elements contents doesn't work on iPad --- jquery.ui.touch-punch.js | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/jquery.ui.touch-punch.js b/jquery.ui.touch-punch.js index 16ce41d..0ac3ba1 100755 --- a/jquery.ui.touch-punch.js +++ b/jquery.ui.touch-punch.js @@ -21,6 +21,9 @@ var mouseProto = $.ui.mouse.prototype, _mouseInit = mouseProto._mouseInit, _mouseDestroy = mouseProto._mouseDestroy, + _mouseDown = mouseProto._mouseDown, + _mouseMove = mouseProto._mouseMove, + touchEvent, touchHandled; /** @@ -35,7 +38,7 @@ return; } - event.preventDefault(); + touchEvent = event; var touch = event.originalEvent.changedTouches[0], simulatedEvent = document.createEvent('MouseEvents'); @@ -177,4 +180,36 @@ _mouseDestroy.call(self); }; + /** + * Hook the $.ui.mouse _mouseDown method so that we can call preventDefault + * on the original touch event if and only if the default handler called + * preventDefault on the simulated mouse event. + */ + mouseProto._mouseDown = function (event) { + + var self = this; + + _mouseDown.call(self, event); + + if (event.isDefaultPrevented()) { + touchEvent.preventDefault(); + } + }; + + /** + * Hook the $.ui.mouse _mouseMove method so that we can call preventDefault + * on the original touch event if and only if the default handler called + * preventDefault on the simulated mouse event. + */ + mouseProto._mouseMove = function (event) { + + var self = this; + + _mouseMove.call(self, event); + + if (event.isDefaultPrevented()) { + touchEvent.preventDefault(); + } + }; + })(jQuery); \ No newline at end of file From 4ba6bc3e2de899dcb77c0e350d65e11a0d39e9a2 Mon Sep 17 00:00:00 2001 From: Rand Scullard Date: Thu, 18 Dec 2014 17:23:39 -0500 Subject: [PATCH 2/5] Fixed issue #41: Scroll inside draggable elements contents doesn't work on iPad (also update minified JavaScript) --- jquery.ui.touch-punch.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jquery.ui.touch-punch.min.js b/jquery.ui.touch-punch.min.js index 31272ce..cc787e0 100644 --- a/jquery.ui.touch-punch.min.js +++ b/jquery.ui.touch-punch.min.js @@ -8,4 +8,4 @@ * jquery.ui.widget.js * jquery.ui.mouse.js */ -!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); \ No newline at end of file +(function(d){d.support.touch="ontouchend" in document;if(!d.support.touch){return;}var c=d.ui.mouse.prototype,f=c._mouseInit,g=c._mouseDestroy,e=c._mouseDown,h=c._mouseMove,b,i;function a(k,l){if(k.originalEvent.touches.length>1){return;}b=k;var m=k.originalEvent.changedTouches[0],j=document.createEvent("MouseEvents");j.initMouseEvent(l,true,true,window,1,m.screenX,m.screenY,m.clientX,m.clientY,false,false,false,false,0,null);k.target.dispatchEvent(j);}c._touchStart=function(k){var j=this;if(i||!j._mouseCapture(k.originalEvent.changedTouches[0])){return;}i=true;j._touchMoved=false;a(k,"mouseover");a(k,"mousemove");a(k,"mousedown");};c._touchMove=function(j){if(!i){return;}this._touchMoved=true;a(j,"mousemove");};c._touchEnd=function(j){if(!i){return;}a(j,"mouseup");a(j,"mouseout");if(!this._touchMoved){a(j,"click");}i=false;};c._mouseInit=function(){var j=this;j.element.bind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});f.call(j);};c._mouseDestroy=function(){var j=this;j.element.unbind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});g.call(j);};c._mouseDown=function(k){var j=this;e.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};c._mouseMove=function(k){var j=this;h.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file From 83663a428eaf72a12c79dae39d3869954e3da954 Mon Sep 17 00:00:00 2001 From: Rand Scullard Date: Fri, 19 Dec 2014 16:34:21 -0500 Subject: [PATCH 3/5] fix41: Call event.preventDefault before simulating a click event, to avoid getting two clicks. --- jquery.ui.touch-punch.js | 3 ++- jquery.ui.touch-punch.min.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/jquery.ui.touch-punch.js b/jquery.ui.touch-punch.js index 0ac3ba1..ea1d826 100755 --- a/jquery.ui.touch-punch.js +++ b/jquery.ui.touch-punch.js @@ -1,4 +1,4 @@ -/*! +/*! * jQuery UI Touch Punch 0.2.3 * * Copyright 2011–2014, Dave Furfero @@ -134,6 +134,7 @@ if (!this._touchMoved) { // Simulate the click event + event.preventDefault(); simulateMouseEvent(event, 'click'); } diff --git a/jquery.ui.touch-punch.min.js b/jquery.ui.touch-punch.min.js index cc787e0..a6107da 100644 --- a/jquery.ui.touch-punch.min.js +++ b/jquery.ui.touch-punch.min.js @@ -8,4 +8,4 @@ * jquery.ui.widget.js * jquery.ui.mouse.js */ -(function(d){d.support.touch="ontouchend" in document;if(!d.support.touch){return;}var c=d.ui.mouse.prototype,f=c._mouseInit,g=c._mouseDestroy,e=c._mouseDown,h=c._mouseMove,b,i;function a(k,l){if(k.originalEvent.touches.length>1){return;}b=k;var m=k.originalEvent.changedTouches[0],j=document.createEvent("MouseEvents");j.initMouseEvent(l,true,true,window,1,m.screenX,m.screenY,m.clientX,m.clientY,false,false,false,false,0,null);k.target.dispatchEvent(j);}c._touchStart=function(k){var j=this;if(i||!j._mouseCapture(k.originalEvent.changedTouches[0])){return;}i=true;j._touchMoved=false;a(k,"mouseover");a(k,"mousemove");a(k,"mousedown");};c._touchMove=function(j){if(!i){return;}this._touchMoved=true;a(j,"mousemove");};c._touchEnd=function(j){if(!i){return;}a(j,"mouseup");a(j,"mouseout");if(!this._touchMoved){a(j,"click");}i=false;};c._mouseInit=function(){var j=this;j.element.bind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});f.call(j);};c._mouseDestroy=function(){var j=this;j.element.unbind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});g.call(j);};c._mouseDown=function(k){var j=this;e.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};c._mouseMove=function(k){var j=this;h.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file +(function(d){d.support.touch="ontouchend" in document;if(!d.support.touch){return;}var c=d.ui.mouse.prototype,f=c._mouseInit,g=c._mouseDestroy,e=c._mouseDown,h=c._mouseMove,b,i;function a(k,l){if(k.originalEvent.touches.length>1){return;}b=k;var m=k.originalEvent.changedTouches[0],j=document.createEvent("MouseEvents");j.initMouseEvent(l,true,true,window,1,m.screenX,m.screenY,m.clientX,m.clientY,false,false,false,false,0,null);k.target.dispatchEvent(j);}c._touchStart=function(k){var j=this;if(i||!j._mouseCapture(k.originalEvent.changedTouches[0])){return;}i=true;j._touchMoved=false;a(k,"mouseover");a(k,"mousemove");a(k,"mousedown");};c._touchMove=function(j){if(!i){return;}this._touchMoved=true;a(j,"mousemove");};c._touchEnd=function(j){if(!i){return;}a(j,"mouseup");a(j,"mouseout");if(!this._touchMoved){j.preventDefault();a(j,"click");}i=false;};c._mouseInit=function(){var j=this;j.element.bind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});f.call(j);};c._mouseDestroy=function(){var j=this;j.element.unbind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});g.call(j);};c._mouseDown=function(k){var j=this;e.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};c._mouseMove=function(k){var j=this;h.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file From c8e60b264cbd38363d515618bc6084def55187a5 Mon Sep 17 00:00:00 2001 From: Rand Scullard Date: Fri, 16 Jan 2015 12:57:41 -0500 Subject: [PATCH 4/5] fix41: Only simulate mouse messages on touchend if we prevented default on touchstart. This avoids duplicating mouse events that the browser simulates on its own. Also improve the code to distinguish between a click and a move. --- jquery.ui.touch-punch.js | 45 ++++++++++++++++++++++++------------ jquery.ui.touch-punch.min.js | 22 +++++++++--------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/jquery.ui.touch-punch.js b/jquery.ui.touch-punch.js index ea1d826..0e26f14 100755 --- a/jquery.ui.touch-punch.js +++ b/jquery.ui.touch-punch.js @@ -1,7 +1,7 @@ /*! * jQuery UI Touch Punch 0.2.3 * - * Copyright 2011–2014, Dave Furfero + * Copyright 2011-2014, Dave Furfero * Dual licensed under the MIT or GPL Version 2 licenses. * * Depends: @@ -24,7 +24,8 @@ _mouseDown = mouseProto._mouseDown, _mouseMove = mouseProto._mouseMove, touchEvent, - touchHandled; + touchHandled, + touchStartDefaultPrevented; /** * Simulate a mouse event based on a corresponding touch event @@ -66,6 +67,17 @@ event.target.dispatchEvent(simulatedEvent); } + /** + * Get the x,y position of a touch event + * @param {Object} event A touch event + */ + function getTouchCoords (event) { + return { + x: event.originalEvent.changedTouches[0].pageX, + y: event.originalEvent.changedTouches[0].pageY + }; + } + /** * Handle the jQuery UI widget's touchstart events * @param {Object} event The widget element's touchstart event @@ -82,8 +94,10 @@ // Set the flag to prevent other widgets from inheriting the touch event touchHandled = true; + touchStartDefaultPrevented = false; + // Track movement to determine if interaction was a click - self._touchMoved = false; + self._startPos = getTouchCoords(event); // Simulate the mouseover event simulateMouseEvent(event, 'mouseover'); @@ -106,9 +120,6 @@ return; } - // Interaction was not a click - this._touchMoved = true; - // Simulate the mousemove event simulateMouseEvent(event, 'mousemove'); }; @@ -124,18 +135,21 @@ return; } - // Simulate the mouseup event - simulateMouseEvent(event, 'mouseup'); + if (touchStartDefaultPrevented) { + + // Simulate the mouseup event + simulateMouseEvent(event, 'mouseup'); - // Simulate the mouseout event - simulateMouseEvent(event, 'mouseout'); + // Simulate the mouseout event + simulateMouseEvent(event, 'mouseout'); - // If the touch interaction did not move, it should trigger a click - if (!this._touchMoved) { + // If the touch interaction did not move, it should trigger a click + var endPos = getTouchCoords(event); + if ((Math.abs(endPos.x - this._startPos.x) < 10) && (Math.abs(endPos.y - this._startPos.y) < 10)) { - // Simulate the click event - event.preventDefault(); - simulateMouseEvent(event, 'click'); + // Simulate the click event + simulateMouseEvent(event, 'click'); + } } // Unset the flag to allow other widgets to inherit the touch event @@ -194,6 +208,7 @@ if (event.isDefaultPrevented()) { touchEvent.preventDefault(); + touchStartDefaultPrevented = true; } }; diff --git a/jquery.ui.touch-punch.min.js b/jquery.ui.touch-punch.min.js index a6107da..8b24e3b 100644 --- a/jquery.ui.touch-punch.min.js +++ b/jquery.ui.touch-punch.min.js @@ -1,11 +1,11 @@ -/*! - * jQuery UI Touch Punch 0.2.3 - * - * Copyright 2011–2014, Dave Furfero - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Depends: - * jquery.ui.widget.js - * jquery.ui.mouse.js - */ -(function(d){d.support.touch="ontouchend" in document;if(!d.support.touch){return;}var c=d.ui.mouse.prototype,f=c._mouseInit,g=c._mouseDestroy,e=c._mouseDown,h=c._mouseMove,b,i;function a(k,l){if(k.originalEvent.touches.length>1){return;}b=k;var m=k.originalEvent.changedTouches[0],j=document.createEvent("MouseEvents");j.initMouseEvent(l,true,true,window,1,m.screenX,m.screenY,m.clientX,m.clientY,false,false,false,false,0,null);k.target.dispatchEvent(j);}c._touchStart=function(k){var j=this;if(i||!j._mouseCapture(k.originalEvent.changedTouches[0])){return;}i=true;j._touchMoved=false;a(k,"mouseover");a(k,"mousemove");a(k,"mousedown");};c._touchMove=function(j){if(!i){return;}this._touchMoved=true;a(j,"mousemove");};c._touchEnd=function(j){if(!i){return;}a(j,"mouseup");a(j,"mouseout");if(!this._touchMoved){j.preventDefault();a(j,"click");}i=false;};c._mouseInit=function(){var j=this;j.element.bind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});f.call(j);};c._mouseDestroy=function(){var j=this;j.element.unbind({touchstart:d.proxy(j,"_touchStart"),touchmove:d.proxy(j,"_touchMove"),touchend:d.proxy(j,"_touchEnd")});g.call(j);};c._mouseDown=function(k){var j=this;e.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};c._mouseMove=function(k){var j=this;h.call(j,k);if(k.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file +/*! + * jQuery UI Touch Punch 0.2.3 + * + * Copyright 2011-2014, Dave Furfero + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Depends: + * jquery.ui.widget.js + * jquery.ui.mouse.js + */ +(function(e){e.support.touch="ontouchend" in document;if(!e.support.touch){return;}var d=e.ui.mouse.prototype,h=d._mouseInit,i=d._mouseDestroy,f=d._mouseDown,j=d._mouseMove,b,k,c;function a(m,n){if(m.originalEvent.touches.length>1){return;}b=m;var o=m.originalEvent.changedTouches[0],l=document.createEvent("MouseEvents");l.initMouseEvent(n,true,true,window,1,o.screenX,o.screenY,o.clientX,o.clientY,false,false,false,false,0,null);m.target.dispatchEvent(l);}function g(l){return{x:l.originalEvent.changedTouches[0].pageX,y:l.originalEvent.changedTouches[0].pageY};}d._touchStart=function(m){var l=this;if(k||!l._mouseCapture(m.originalEvent.changedTouches[0])){return;}k=true;c=false;l._startPos=g(m);a(m,"mouseover");a(m,"mousemove");a(m,"mousedown");};d._touchMove=function(l){if(!k){return;}a(l,"mousemove");};d._touchEnd=function(m){if(!k){return;}if(c){a(m,"mouseup");a(m,"mouseout");var l=g(m);if((Math.abs(l.x-this._startPos.x)<10)&&(Math.abs(l.y-this._startPos.y)<10)){a(m,"click");}}k=false;};d._mouseInit=function(){var l=this;l.element.bind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});h.call(l);};d._mouseDestroy=function(){var l=this;l.element.unbind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});i.call(l);};d._mouseDown=function(m){var l=this;f.call(l,m);if(m.isDefaultPrevented()){b.preventDefault();c=true;}};d._mouseMove=function(m){var l=this;j.call(l,m);if(m.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file From 250a9e585de0441fc133fd72c5745050de9a3dde Mon Sep 17 00:00:00 2001 From: Rand Scullard Date: Thu, 7 May 2015 09:25:31 -0400 Subject: [PATCH 5/5] fix41: Handle the case where the user is using a mouse on a browser that supports touch events, causing touchEvent to be undefined in _mouseDown and _mouseMove. --- jquery.ui.touch-punch.js | 4 ++-- jquery.ui.touch-punch.min.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jquery.ui.touch-punch.js b/jquery.ui.touch-punch.js index 0e26f14..1197f26 100755 --- a/jquery.ui.touch-punch.js +++ b/jquery.ui.touch-punch.js @@ -206,7 +206,7 @@ _mouseDown.call(self, event); - if (event.isDefaultPrevented()) { + if (event.isDefaultPrevented() && touchEvent) { touchEvent.preventDefault(); touchStartDefaultPrevented = true; } @@ -223,7 +223,7 @@ _mouseMove.call(self, event); - if (event.isDefaultPrevented()) { + if (event.isDefaultPrevented() && touchEvent) { touchEvent.preventDefault(); } }; diff --git a/jquery.ui.touch-punch.min.js b/jquery.ui.touch-punch.min.js index 8b24e3b..fe5caa7 100644 --- a/jquery.ui.touch-punch.min.js +++ b/jquery.ui.touch-punch.min.js @@ -8,4 +8,4 @@ * jquery.ui.widget.js * jquery.ui.mouse.js */ -(function(e){e.support.touch="ontouchend" in document;if(!e.support.touch){return;}var d=e.ui.mouse.prototype,h=d._mouseInit,i=d._mouseDestroy,f=d._mouseDown,j=d._mouseMove,b,k,c;function a(m,n){if(m.originalEvent.touches.length>1){return;}b=m;var o=m.originalEvent.changedTouches[0],l=document.createEvent("MouseEvents");l.initMouseEvent(n,true,true,window,1,o.screenX,o.screenY,o.clientX,o.clientY,false,false,false,false,0,null);m.target.dispatchEvent(l);}function g(l){return{x:l.originalEvent.changedTouches[0].pageX,y:l.originalEvent.changedTouches[0].pageY};}d._touchStart=function(m){var l=this;if(k||!l._mouseCapture(m.originalEvent.changedTouches[0])){return;}k=true;c=false;l._startPos=g(m);a(m,"mouseover");a(m,"mousemove");a(m,"mousedown");};d._touchMove=function(l){if(!k){return;}a(l,"mousemove");};d._touchEnd=function(m){if(!k){return;}if(c){a(m,"mouseup");a(m,"mouseout");var l=g(m);if((Math.abs(l.x-this._startPos.x)<10)&&(Math.abs(l.y-this._startPos.y)<10)){a(m,"click");}}k=false;};d._mouseInit=function(){var l=this;l.element.bind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});h.call(l);};d._mouseDestroy=function(){var l=this;l.element.unbind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});i.call(l);};d._mouseDown=function(m){var l=this;f.call(l,m);if(m.isDefaultPrevented()){b.preventDefault();c=true;}};d._mouseMove=function(m){var l=this;j.call(l,m);if(m.isDefaultPrevented()){b.preventDefault();}};})(jQuery); \ No newline at end of file +(function(e){e.support.touch="ontouchend" in document;if(!e.support.touch){return;}var d=e.ui.mouse.prototype,h=d._mouseInit,i=d._mouseDestroy,f=d._mouseDown,j=d._mouseMove,b,k,c;function a(m,n){if(m.originalEvent.touches.length>1){return;}b=m;var o=m.originalEvent.changedTouches[0],l=document.createEvent("MouseEvents");l.initMouseEvent(n,true,true,window,1,o.screenX,o.screenY,o.clientX,o.clientY,false,false,false,false,0,null);m.target.dispatchEvent(l);}function g(l){return{x:l.originalEvent.changedTouches[0].pageX,y:l.originalEvent.changedTouches[0].pageY};}d._touchStart=function(m){var l=this;if(k||!l._mouseCapture(m.originalEvent.changedTouches[0])){return;}k=true;c=false;l._startPos=g(m);a(m,"mouseover");a(m,"mousemove");a(m,"mousedown");};d._touchMove=function(l){if(!k){return;}a(l,"mousemove");};d._touchEnd=function(m){if(!k){return;}if(c){a(m,"mouseup");a(m,"mouseout");var l=g(m);if((Math.abs(l.x-this._startPos.x)<10)&&(Math.abs(l.y-this._startPos.y)<10)){a(m,"click");}}k=false;};d._mouseInit=function(){var l=this;l.element.bind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});h.call(l);};d._mouseDestroy=function(){var l=this;l.element.unbind({touchstart:e.proxy(l,"_touchStart"),touchmove:e.proxy(l,"_touchMove"),touchend:e.proxy(l,"_touchEnd")});i.call(l);};d._mouseDown=function(m){var l=this;f.call(l,m);if(m.isDefaultPrevented()&&b){b.preventDefault();c=true;}};d._mouseMove=function(m){var l=this;j.call(l,m);if(m.isDefaultPrevented()&&b){b.preventDefault();}};})(jQuery); \ No newline at end of file