diff --git a/resources/static/common/js/modules/xhr_disable_form.js b/resources/static/common/js/modules/xhr_disable_form.js index 0d7f943b41cd084099137c26f3b43fea041b5368..db96d112602f866baea4bf43c655fc672ae75e58 100644 --- a/resources/static/common/js/modules/xhr_disable_form.js +++ b/resources/static/common/js/modules/xhr_disable_form.js @@ -11,20 +11,39 @@ BrowserID.Modules.XHRDisableForm = (function() { var Module = bid.Modules.PageModule.extend({ start: function(options) { - var self=this; + var self=this, + enableDelayMS = options.enableDelayMS || 100; + + function cancelRemoveClassDelay() { + if (self.enableDelay) { + clearTimeout(self.enableDelay); + self.enableDelay = null; + } + } + + self.subscribe("xhr_start", function() { + // A new XHR request has started since the enableDelay was + // started. Since the timeout has not yet completed, cancel it so the + // button does not flicker. + cancelRemoveClassDelay(); + dom.addClass("body", "submit_disabled"); + }); - self.subscribe("xhr_start", - dom.addClass.curry("body", "submit_disabled")); self.subscribe("xhr_complete", function() { // Add a small delay between the time the XHR is complete and when the // submit_disabled class is actually removed. This helps reduce the // amount of flicker the user sees if one XHR request completes and // another one starts immediately afterwards. // See https://github.com/mozilla/browserid/issues/1898 - setTimeout(function() { + + // If multiple xhr_completes come in, the class should be removed after + // the timeout of the LAST completion. Cancel any that are outstanding. + cancelRemoveClassDelay(); + self.enableDelay = setTimeout(function() { dom.removeClass("body", "submit_disabled"); + self.enableDelay = null; self.publish("submit_enabled"); - }, 100); + }, enableDelayMS); }); sc.start.call(self, options); diff --git a/resources/static/test/cases/common/js/modules/xhr_disable_form.js b/resources/static/test/cases/common/js/modules/xhr_disable_form.js index 71efa49c4dde6ce1e93a87604b0460caf398e704..2f88ae869803be7187063191bd5fe91b58787aa0 100644 --- a/resources/static/test/cases/common/js/modules/xhr_disable_form.js +++ b/resources/static/test/cases/common/js/modules/xhr_disable_form.js @@ -18,10 +18,10 @@ return mod; } - module("shared/modules/xhr_disable_form", { + module("common/js/modules/xhr_disable_form", { setup: function() { testHelpers.setup(); - createModule(); + createModule({ enableDelayMS: 10 }); }, teardown: function() { @@ -44,4 +44,35 @@ }); mediator.publish("xhr_complete"); }); + + asyncTest("multiple xhr_completes only cause one submit_enabled", function() { + var submitEnabledCount = 0; + mediator.subscribe("submit_enabled", function() { + submitEnabledCount++; + }); + mediator.publish("xhr_complete"); + mediator.publish("xhr_complete"); + + // give plenty of time to allow all submit_enabled timeouts to occur. + setTimeout(function() { + equal(submitEnabledCount, 1, "submit_enabled called only once"); + start(); + }, 50); + }); + + asyncTest("xhr_start after xhr_complete but before submit_enabled cancels submit_enabled", function() { + var submitEnabledCount = 0; + mediator.subscribe("submit_enabled", function() { + submitEnabledCount++; + }); + mediator.publish("xhr_complete"); + mediator.publish("xhr_start"); + + // give plenty of time to allow all submit_enabled timeouts to occur. + setTimeout(function() { + equal(submitEnabledCount, 0, "submit_enabled cancelled after xhr_start"); + start(); + }, 50); + }); + }());