//Timer on Deferreds (function (){ var top = this; var defaults = { granularity : 500, name : 'default' }; this.Timer = function(timeout, options){ var o = $.extend({}, defaults, options), granularity = o.granularity, name = o.name; var coreFunctionality, timeoutFuncRef, tickRef; var timeStarted = new Date() - 0, timeElapsed = 0, lastKnownTime = timeStarted, isRunning = false; return $.Deferred(function(def){ coreFunctionality = { pause: function(){ if (!isRunning) return; var time = new Date() - 0, elapsed = time - lastKnownTime; timeElapsed += elapsed; lastKnownTime = time; top.clearTimeout(timeoutFuncRef); timeoutFuncRef = null; isRunning = false; }, resume: function(){ if (isRunning) return; lastKnownTime = new Date() - 0; timeoutFuncRef = window.setTimeout(def.resolve, timeout - timeElapsed); isRunning = true; }, stop: function(){ def.resolve(); cleanup(); }, }; timeoutFuncRef = window.setTimeout(def.resolve, timeout - timeElapsed); tickRef = window.setInterval(function(){ if (!isRunning) return; var time = new Date() - 0, elapsed = time - lastKnownTime; timeElapsed += elapsed; lastKnownTime = time; def.notify({ name: name, total: timeout, elapsed: timeElapsed }); }, granularity); isRunning = true; }).promise(coreFunctionality); function cleanup(){ top.clearTimeout(timeoutFuncRef); timeoutFuncRef = null; top.clearInterval(tickRef); tickRef = null; isRunning = false; } }; })();