From 6759afff6697fcbefaa3f0facdc80d3cec450547 Mon Sep 17 00:00:00 2001 From: Kin Blas Date: Thu, 1 Sep 2011 14:17:10 -0700 Subject: [PATCH 1/7] Fix for issue 1925 - Single tap triggers two actions, especially in android B1 - Trigger the list item and keyboard return/space key up to the "click" event instead of "vclick". This delays the dismissal of the custom select menu until the click event, thereby avoiding the case where the menu disappears before the browser dispatches it's synthesized mouse events (in the touch case) with a target of whatever element was underneath the menu. --- js/jquery.mobile.forms.select.custom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/jquery.mobile.forms.select.custom.js b/js/jquery.mobile.forms.select.custom.js index d62e1f1f..d94c8994 100644 --- a/js/jquery.mobile.forms.select.custom.js +++ b/js/jquery.mobile.forms.select.custom.js @@ -103,7 +103,7 @@ .delegate( ".ui-li>a", "focusout", function() { $( this ).attr( "tabindex", "-1" ); }) - .delegate( "li:not(.ui-disabled, .ui-li-divider)", "vclick", function( event ) { + .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) { // index of option tag to be selected var oldIndex = self.select[ 0 ].selectedIndex, @@ -174,7 +174,7 @@ // If enter or space is pressed, trigger click case 13: case 32: - target.trigger( "vclick" ); + target.trigger( "click" ); return false; break; From a27f7d355f59f4aebef2465c5a3b7c95d1453d43 Mon Sep 17 00:00:00 2001 From: Kin Blas Date: Thu, 1 Sep 2011 15:28:20 -0700 Subject: [PATCH 2/7] Fix for issue 2346 - Flip toggle switches don't animate when tapped. - Changed the target selector for ui-slider-handle-snapping from: div.ui-slider-handle-snapping to: a.ui-slider-handle-snapping to match the actual markup being generated. --- themes/default/jquery.mobile.forms.slider.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/default/jquery.mobile.forms.slider.css b/themes/default/jquery.mobile.forms.slider.css index b34455e9..62b76723 100644 --- a/themes/default/jquery.mobile.forms.slider.css +++ b/themes/default/jquery.mobile.forms.slider.css @@ -16,7 +16,7 @@ a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; } div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; } div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; } -div.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; } +a.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; } div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; } div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; } div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; } From 7bf048a214b635c66f882ffddba80b27698ebbc3 Mon Sep 17 00:00:00 2001 From: Kin Blas Date: Thu, 1 Sep 2011 15:42:30 -0700 Subject: [PATCH 3/7] I noticed that the code in the mouseup for the toggle switch was using a CSS3 transition, but calling the animationComplete() function which waits for an animationend event which will never come. This means we bind a new animationComplete() handler every time the toggle switch is clicked. I removed the binding for the handler completely since it doesn't hurt to just leave the transition in place. I also removed the code that was setting the position of the slider since it really should only be set within the mouseup if the user did not change the switch setting (toggle). --- js/jquery.mobile.forms.slider.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/js/jquery.mobile.forms.slider.js b/js/jquery.mobile.forms.slider.js index 13ac9d86..9195fca1 100644 --- a/js/jquery.mobile.forms.slider.js +++ b/js/jquery.mobile.forms.slider.js @@ -140,16 +140,9 @@ $.widget( "mobile.slider", $.mobile.widget, { if ( !self.userModified ) { //tap occurred, but value didn't change. flip it! + handle.addClass( "ui-slider-handle-snapping" ); self.refresh( !self.beforeStart ? 1 : 0 ); } - var curval = val(); - var snapped = Math.round( curval / ( max - min ) * 100 ); - handle - .addClass( "ui-slider-handle-snapping" ) - .css( "left", snapped + "%" ) - .animationComplete( function() { - handle.removeClass( "ui-slider-handle-snapping" ); - }); } return false; } From c3dfcf6bdff99af040ec9a98cba468e18068116f Mon Sep 17 00:00:00 2001 From: John Bender Date: Fri, 2 Sep 2011 13:40:04 -0700 Subject: [PATCH 4/7] fixed event tests to trigger on a dom element --- tests/unit/event/event_core.js | 77 ++++++++++++++++------------------ 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/tests/unit/event/event_core.js b/tests/unit/event/event_core.js index f115b2c5..2f606836 100644 --- a/tests/unit/event/event_core.js +++ b/tests/unit/event/event_core.js @@ -14,20 +14,15 @@ setup: function(){ // ensure bindings are removed - $.each(events, function(i, name){ - $.each([$("#qunit-fixture"), - $($.event.special.scrollstart), - $($.event.special.tap), - $($.event.special.tap), - $($.event.special.swipe)], function(j, obj){ - obj.unbind(name); - }); + $.each(events + "vmouseup vmousedown".split(" "), function(i, name){ + $("#qunit-fixture").unbind(); }); //NOTE unmock Math.abs = absFn; $.Event.prototype.originalEvent = originalEventFn; $.Event.prototype.preventDefault = preventDefaultFn; + $.Event.prototype.touches = [{pageX: 1, pageY: 1 }]; $($.mobile.pageContainer).unbind( "throttledresize" ); } @@ -43,7 +38,7 @@ $.testHelper.reloadLib(libName); $.each(events, function( i, name ) { - ok($.fn[name] !== undefined, name + "is not undefined"); + ok($.fn[name] !== undefined, name + " is not undefined"); }); }); }); @@ -88,40 +83,40 @@ expect( 1 ); $.event.special.scrollstart.enabled = false; - $($.event.special.scrollstart).bind("scrollstart", function(){ + $( "#qunit-fixture" ).bind("scrollstart", function(){ ok(false, "scrollstart fired"); }); - $($.event.special.scrollstart).bind("touchmove", function(){ + $( "#qunit-fixture" ).bind("touchmove", function(){ ok(true, "touchmove fired"); start(); }); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); asyncTest( "scrollstart setup binds a function that triggers scroll start when enabled", function(){ $.event.special.scrollstart.enabled = true; - $($.event.special.scrollstart).bind("scrollstart", function(){ + $( "#qunit-fixture" ).bind("scrollstart", function(){ ok(true, "scrollstart fired"); start(); }); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); asyncTest( "scrollstart setup binds a function that triggers scroll stop after 50 ms", function(){ var triggered = false; $.event.special.scrollstart.enabled = true; - $($.event.special.scrollstart).bind("scrollstop", function(){ + $( "#qunit-fixture" ).bind("scrollstop", function(){ triggered = true; }); ok(!triggered, "not triggered"); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); setTimeout(function(){ ok(triggered, "triggered"); @@ -144,11 +139,11 @@ forceTouchSupport(); - $($.event.special.tap).bind("taphold", function(){ + $( "#qunit-fixture" ).bind("taphold", function(){ taphold = true; }); - $($.event.special.tap).trigger("vmousedown"); + $( "#qunit-fixture" ).trigger("vmousedown"); setTimeout(function(){ ok(taphold); @@ -172,17 +167,17 @@ mockAbs(100); //NOTE record taphold event - $($.event.special.tap).bind("taphold", function(){ + $( "#qunit-fixture" ).bind("taphold", function(){ ok(false, "taphold fired"); taphold = true; }); //NOTE start the touch events - $($.event.special.tap).trigger("vmousedown"); + $( "#qunit-fixture" ).trigger("vmousedown"); //NOTE fire touchmove to push back taphold setTimeout(function(){ - $($.event.special.tap).trigger("vmousecancel"); + $( "#qunit-fixture" ).trigger("vmousecancel"); }, 100); //NOTE verify that the taphold hasn't been fired @@ -203,11 +198,11 @@ forceTouchSupport(); //NOTE record the tap event - $($.event.special.tap).bind("tap", checkTap); + $( "#qunit-fixture" ).bind("tap", checkTap); - $($.event.special.tap).trigger("vmousedown"); - $($.event.special.tap).trigger("vmouseup"); - $($.event.special.tap).trigger("vclick"); + $( "#qunit-fixture" ).trigger("vmousedown"); + $( "#qunit-fixture" ).trigger("vmouseup"); + $( "#qunit-fixture" ).trigger("vclick"); setTimeout(function(){ start(); @@ -220,7 +215,7 @@ forceTouchSupport(); //NOTE record tap event - $($.event.special.tap).bind("tap", function(){ + $( "#qunit-fixture" ).bind("tap", function(){ ok(false, "tap fired"); tap = true; }); @@ -229,12 +224,12 @@ mockAbs(100); //NOTE start and move right away - $($.event.special.tap).trigger("touchstart"); - $($.event.special.tap).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchmove"); //NOTE end touch sequence after 20 ms setTimeout(function(){ - $($.event.special.tap).trigger("touchend"); + $( "#qunit-fixture" ).trigger("touchend"); }, 20); setTimeout(function(){ @@ -248,7 +243,7 @@ forceTouchSupport(); - $($.event.special.swipe).bind('swipe', function(){ + $( "#qunit-fixture" ).bind('swipe', function(){ swipe = true; }); @@ -257,15 +252,15 @@ touches: false }; - $($.event.special.swipe).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchstart"); //NOTE make sure the coordinates are calculated within range // to be registered as a swipe mockAbs(opts.coordChange); setTimeout(function(){ - $($.event.special.swipe).trigger("touchmove"); - $($.event.special.swipe).trigger("touchend"); + $( "#qunit-fixture" ).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchend"); }, opts.timeout + 100); setTimeout(function(){ @@ -298,7 +293,7 @@ forceTouchSupport(); // ensure the swipe custome event is setup - $($.event.special.swipe).bind('swipe', function(){}); + $( "#qunit-fixture" ).bind('swipe', function(){}); //NOTE bypass the trigger source check $.Event.prototype.originalEvent = { @@ -312,8 +307,8 @@ mockAbs(11); - $($.event.special.swipe).trigger("touchstart"); - $($.event.special.swipe).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchmove"); }); asyncTest( "move handler returns when touchstart has been fired since touchstop", function(){ @@ -327,12 +322,12 @@ forceTouchSupport(); // ensure the swipe custome event is setup - $($.event.special.swipe).bind('swipe', function(){}); + $( "#qunit-fixture" ).bind('swipe', function(){}); - $($.event.special.swipe).trigger("touchstart"); - $($.event.special.swipe).trigger("touchend"); + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchend"); - $($.event.special.swipe).bind("touchmove", function(){ + $( "#qunit-fixture" ).bind("touchmove", function(){ ok(true, "touchmove bound functions are fired"); start(); }); @@ -341,7 +336,7 @@ ok(false, "shouldn't compare coordinates"); }; - $($.event.special.swipe).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); var nativeSupportTest = function(opts){ From 0b41fe808c630277c1710c16c2d828e6fad72229 Mon Sep 17 00:00:00 2001 From: John Bender Date: Fri, 2 Sep 2011 13:56:37 -0700 Subject: [PATCH 5/7] added comment for the touches event proto addition --- tests/unit/event/event_core.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/event/event_core.js b/tests/unit/event/event_core.js index 2f606836..320bf183 100644 --- a/tests/unit/event/event_core.js +++ b/tests/unit/event/event_core.js @@ -22,6 +22,9 @@ Math.abs = absFn; $.Event.prototype.originalEvent = originalEventFn; $.Event.prototype.preventDefault = preventDefaultFn; + + // make sure the event objects respond to touches to simulate + // the collections existence in non touch enabled test browsers $.Event.prototype.touches = [{pageX: 1, pageY: 1 }]; $($.mobile.pageContainer).unbind( "throttledresize" ); From 7cce0c5573ad593b17302082e936b10f568551a4 Mon Sep 17 00:00:00 2001 From: Kin Blas Date: Fri, 2 Sep 2011 16:56:15 -0700 Subject: [PATCH 6/7] Fixes for issue 1464 - No way to stop a link from being followed with some custom event (tap, taphold) jquery.mobile.vmouse.js: - Modified triggerVirtualEvent() so that it returns the virtual event instead of the isDefaultPrevented() result of the virtual event. - Updated all references to triggerVirtualEvent() that relied on the boolean return value to instead check the isDefaultPrevented() call on the event now returned. - Updated mouseEventCallback() to propagate the iDefaultPrevented(), isPropagationStopped(), and stopImmediatePropagation() values from the virtual event on to the original mouse event. jquery.mobile.event.js - Modified the "taphold" trigger code to create a new $.Event() instead of passing the stale vmousedown event. - Added clearTapTimer() which is called from a new vmouseup binding, to prevent the timer from firing between the tie the finger/mouse goes up and the click event is dispatched. - Added some propagation tests for the "tap" event. Tests for "taphold" will have to wait until we fix the problem where multiple taphold timers are fired off when an element and one of its ancestors is bound to taphold. --- js/jquery.mobile.event.js | 16 +++--- js/jquery.mobile.vmouse.js | 23 ++++++--- tests/unit/event/event_core.js | 89 ++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/js/jquery.mobile.event.js b/js/jquery.mobile.event.js index d26c51d1..03bffaab 100644 --- a/js/jquery.mobile.event.js +++ b/js/jquery.mobile.event.js @@ -78,16 +78,19 @@ $.event.special.tap = { return false; } - var touching = true, - origTarget = event.target, + var origTarget = event.target, origEvent = event.originalEvent, timer; + function clearTapTimer() { + clearTimeout( timer ); + } + function clearTapHandlers() { - touching = false; - clearTimeout(timer); + clearTapTimer(); $this.unbind( "vclick", clickHandler ) + .unbind( "vmouseup", clearTapTimer ) .unbind( "vmousecancel", clearTapHandlers ); } @@ -102,12 +105,11 @@ $.event.special.tap = { } $this.bind( "vmousecancel", clearTapHandlers ) + .bind( "vmouseup", clearTapTimer ) .bind( "vclick", clickHandler ); timer = setTimeout(function() { - if ( touching ) { - triggerCustomEvent( thisObject, "taphold", event ); - } + triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) ); }, 750 ); }); } diff --git a/js/jquery.mobile.vmouse.js b/js/jquery.mobile.vmouse.js index f205f328..47e9823a 100644 --- a/js/jquery.mobile.vmouse.js +++ b/js/jquery.mobile.vmouse.js @@ -164,8 +164,7 @@ function clearResetTimer() { } function triggerVirtualEvent( eventType, event, flags ) { - var defaultPrevented = false, - ve; + var ve; if ( ( flags && flags[ eventType ] ) || ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { @@ -173,18 +172,27 @@ function triggerVirtualEvent( eventType, event, flags ) { ve = createVirtualEvent( event, eventType ); $( event.target).trigger( ve ); - - defaultPrevented = ve.isDefaultPrevented(); } - return defaultPrevented; + return ve; } function mouseEventCallback( event ) { var touchID = $.data(event.target, touchTargetPropertyName); if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){ - triggerVirtualEvent( "v" + event.type, event ); + var ve = triggerVirtualEvent( "v" + event.type, event ); + if ( ve ) { + if ( ve.isDefaultPrevented() ) { + event.preventDefault(); + } + if ( ve.isPropagationStopped() ) { + event.stopPropagation(); + } + if ( ve.isImmediatePropagationStopped() ) { + event.stopImmediatePropagation(); + } + } } } @@ -264,7 +272,8 @@ function handleTouchEnd( event ) { triggerVirtualEvent( "vmouseup", event, flags ); if ( !didScroll ) { - if ( triggerVirtualEvent( "vclick", event, flags ) ) { + var ve = triggerVirtualEvent( "vclick", event, flags ); + if ( ve && ve.isDefaultPrevented() ) { // The target of the mouse events that follow the touchend // event don't necessarily match the target used during the // touch. This means we need to rely on coordinates for blocking diff --git a/tests/unit/event/event_core.js b/tests/unit/event/event_core.js index 320bf183..a42489d9 100644 --- a/tests/unit/event/event_core.js +++ b/tests/unit/event/event_core.js @@ -241,6 +241,95 @@ }, 40); }); + asyncTest( "tap event propagates up DOM tree", function(){ + var tap = 0, + $qf = $( "#qunit-fixture" ), + $doc = $( document ), + docTapCB = function(){ + same(++tap, 2, "document tap callback called once after #qunit-fixture callback"); + }; + + $qf.bind( "tap", function() { + same(++tap, 1, "#qunit-fixture tap callback called once"); + }); + + $doc.bind( "tap", docTapCB ); + + $qf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered twice, once for + // #qunit-fixture, and a second time for document. + same( tap, 2, "final tap callback count is 2" ); + + $doc.unbind( "tap", docTapCB ); + + start(); + }); + + asyncTest( "stopPropagation() prevents tap from propagating up DOM tree", function(){ + var tap = 0, + $qf = $( "#qunit-fixture" ), + $doc = $( document ), + docTapCB = function(){ + ok(false, "tap should NOT be triggered on document"); + }; + + $qf.bind( "tap", function(e) { + same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture"); + e.stopPropagation(); + }) + .bind( "tap", function(e) { + same(++tap, 2, "tap callback 2 triggered once on #qunit-fixture"); + }); + + $doc.bind( "tap", docTapCB); + + $qf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered twice. + same( tap, 2, "final tap count is 2" ); + + $doc.unbind( "tap", docTapCB ); + + start(); + }); + + asyncTest( "stopImmediatePropagation() prevents tap propagation and execution of 2nd handler", function(){ + var tap = 0, + $cf = $( "#qunit-fixture" ); + $doc = $( document ), + docTapCB = function(){ + ok(false, "tap should NOT be triggered on document"); + }; + + // Bind 2 tap callbacks on qunit-fixture. Only the first + // one should ever be called. + $cf.bind( "tap", function(e) { + same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture"); + e.stopImmediatePropagation(); + }) + .bind( "tap", function(e) { + ok(false, "tap callback 2 should NOT be triggered on #qunit-fixture"); + }); + + $doc.bind( "tap", docTapCB); + + $cf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered once. + same( tap, 1, "final tap count is 1" ); + + $doc.unbind( "tap", docTapCB ); + + start(); + }); + var swipeTimedTest = function(opts){ var swipe = false; From a4b9079cd7e0c92547212313f8c5683d24e34e02 Mon Sep 17 00:00:00 2001 From: dtao Date: Tue, 6 Sep 2011 16:19:06 -0700 Subject: [PATCH 7/7] fixed typo: 'throttledResize' => 'throttledresize' --- js/jquery.mobile.media.classes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/jquery.mobile.media.classes.js b/js/jquery.mobile.media.classes.js index 3951e283..73def281 100644 --- a/js/jquery.mobile.media.classes.js +++ b/js/jquery.mobile.media.classes.js @@ -77,7 +77,7 @@ $( document ).bind( "mobileinit.htmlclass", function() { var ev = $.support.orientation; - $window.bind( "orientationchange.htmlclass throttledResize.htmlclass", function( event ) { + $window.bind( "orientationchange.htmlclass throttledresize.htmlclass", function( event ) { // add orientation class to HTML element on flip/resize. if ( event.orientation ) {