diff --git a/src/ngTouch/directive/ngClick.js b/src/ngTouch/directive/ngClick.js index f352c252f443..0b1af1e3fe66 100644 --- a/src/ngTouch/directive/ngClick.js +++ b/src/ngTouch/directive/ngClick.js @@ -57,6 +57,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement', var ACTIVE_CLASS_NAME = 'ng-click-active'; var lastPreventedTime; + var processingTime; var touchCoordinates; var lastLabelClickCoordinates; @@ -121,7 +122,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement', // Global click handler that prevents the click if it's in a bustable zone and preventGhostClick // was called recently. function onClick(event) { - if (Date.now() - lastPreventedTime > PREVENT_DURATION) { + if (Date.now() - lastPreventedTime - processingTime > PREVENT_DURATION) { return; // Too old. } @@ -197,6 +198,14 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement', checkAllowableRegions(touchCoordinates, x, y); } + function calculateProcessingTime() { + //use the now - last prevented time on a timeout + processingTime = 0; + $timeout(function() { + processingTime = Date.now() - lastPreventedTime; + }, 0); + } + // Actual linking function. return function(scope, element, attr) { var clickHandler = $parse(attr.ngClick), @@ -251,6 +260,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement', if (tapping && diff < TAP_DURATION && dist < MOVE_TOLERANCE) { // Call preventGhostClick so the clickbuster will catch the corresponding click. preventGhostClick(x, y); + calculateProcessingTime(); // Blur the focused element (the button, probably) before firing the callback. // This doesn't work perfectly on Android Chrome, but seems to work elsewhere. diff --git a/test/ngTouch/directive/ngClickSpec.js b/test/ngTouch/directive/ngClickSpec.js index 179b0248ff21..b1bcca12102c 100644 --- a/test/ngTouch/directive/ngClickSpec.js +++ b/test/ngTouch/directive/ngClickSpec.js @@ -233,6 +233,85 @@ describe('ngClick (touch)', function() { })); + it('should cancel the following click event with long running processes', inject(function($rootScope, $compile, $rootElement, $timeout) { + //we need the real date now function for really getting the time... to really test the timeout... + Date.now = orig_now; + var count = 0; + var done = false; + + $rootScope.slowCount = function() { + var start = Date.now(); + var now = Date.now(); + //must wait longer than the PREVENT_DURATION + while (now - start < 3000) { + now = Date.now(); + } + count++; + }; + + element = $compile('
')($rootScope); + $rootElement.append(element); + + $rootScope.$digest(); + + expect(count).toBe(0); + + // Fire touchstart at 10ms, touchend at 50ms, the click at 300ms. + runs(function() { + setTimeout(function() { + browserTrigger(element, 'touchstart', { + keys: [], + x: 10, + y: 10 + }); + try { + $timeout.verifyNoPendingTasks(); + } catch (e) { + $timeout.flush(); + } + }, 10); + + setTimeout(function() { + browserTrigger(element, 'touchend', { + keys: [], + x: 10, + y: 10 + }); + try { + $timeout.verifyNoPendingTasks(); + } catch (e) { + $timeout.flush(); + } + + expect(count).toBe(1); + }, 50); + + setTimeout(function() { + browserTrigger(element, 'click', { + keys: [], + x: 10, + y: 10 + }); + try { + $timeout.verifyNoPendingTasks(); + } catch (e) { + $timeout.flush(); + } + done = true; + }, 300); + + }); + + waitsFor(function() { + return done; + }, "click event to fire", 500); + + runs(function() { + expect(count).toEqual(1); + }); + })); + + it('should cancel the following click event even when the element has changed', inject( function($rootScope, $compile, $rootElement) { $rootElement.append(