diff --git a/jquery.stopwatch.js b/jquery.stopwatch.js index eeeb48b..4f223a7 100644 --- a/jquery.stopwatch.js +++ b/jquery.stopwatch.js @@ -1,7 +1,20 @@ (function( $ ){ function incrementer(ct, increment) { - return function() { ct+=increment; return ct; }; + var startDate = new Date(); + var startCt = ct; + // Make sure we have an actual number for startCt + if (!startCt) { + startCt = 0; + } + var result = function() { + return startCt + Math.round((new Date() - startDate)/increment)*increment; + }; + result.restart = function(newCt) { + startCt = newCt; + startDate = new Date(); + } + return result; } function pad2(number) { @@ -79,6 +92,9 @@ var $this = $(this), data = $this.data('stopwatch'); // Mark as active + if(!data.active) { + data.incrementer.restart(data.elapsed); + } data.active = true; data.timerID = setInterval(data.tick_function, data.updateInterval); $this.data('stopwatch', data); diff --git a/test/test.js b/test/test.js index 7f505a7..7449e03 100644 --- a/test/test.js +++ b/test/test.js @@ -62,6 +62,34 @@ test("Stop", function(){ equals($elem.data('stopwatch').active, false); }); +test("Stop and then Start", function(){ + var interval = $elem.data('stopwatch').updateInterval; + // Start the stopwatch for one tick, stop it for more than two ticks, then + // restart it for one more tick. The elapsed time should be two ticks, even + // though three and a half ticks worth of actual wall clock time went by. + var firstTick = true; + var restart = function() { + // in-between waiting time is over; restart the stopwatch + $elem.stopwatch('start'); + } + $elem.stopwatch().bind('tick.stopwatch', function(e, elapsed) { + if (firstTick) { + // First tick: stop and wait 2.5 ticks + equals($elem.stopwatch('getTime'), interval); + $elem.stopwatch('stop'); + setTimeout(restart, Math.floor(interval*2.5)); + } else { + // Second tick: stop the stopwatch, elapsed should be 2 ticks + equals($elem.stopwatch('getTime'), interval * 2); + $elem.stopwatch('stop'); + start(); + } + firstTick = false; + }); + stop(); + $elem.stopwatch('start'); +}); + test("Toggle", function(){ $elem.stopwatch('stop'); $elem.stopwatch('toggle'); @@ -185,3 +213,33 @@ test("Custom update interval", function(){ start(); }, 2000); }); + +test("Slow callback should not affect accuracy", function(){ + $elem.stopwatch('init', {updateInterval: 100}); + // We're going to sleep for 50 millisecs, with the stopwatch running + // on a 10-milisecond interval. The result should be two ticks: + // the first after 10 milliseconds, and the next after 60. The second + // one should nevertheless report a 60 millisec elapsed time. + var ticksEncountered = 0; + var data = $elem.data('stopwatch'); + var tickFunction; + tickFunction = function(e, elapsed) { + if (ticksEncountered >= 1) { + equals(ticksEncountered, 1); + equals(data.elapsed, 600); + $elem.stopwatch().unbind('tick.stopwatch', tickFunction); + start(); + } else { + // Yes, this is a ridiculous busy-wait. But the point is to test + // for callbacks that inherently take longer than our interval to + // complete. + var startDt = new Date(); + var currDt = null; + do { currDt = new Date(); } while (currDt - startDt < 500); + } + ++ticksEncountered; + }; + $elem.stopwatch().bind('tick.stopwatch', tickFunction); + stop(); + $elem.stopwatch('start'); +});