15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved.
25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Use of this source code is governed by a BSD-style license that can be
35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// found in the LICENSE file.
45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @fileoverview Provides a countdown-based timer.
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict';
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * A countdown timer.
125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @interface
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction Countdown() {}
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Sets a new timeout for this timer.
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts.
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function=} cb called back when the countdown expires.
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} whether the timeout could be set.
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdown.prototype.setTimeout = function(timeoutMillis, cb) {};
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Clears this timer's timeout. */
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdown.prototype.clearTimeout = function() {};
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {number} how many milliseconds are remaining until the timer expires.
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdown.prototype.millisecondsUntilExpired = function() {};
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** @return {boolean} whether the timer has expired. */
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdown.prototype.expired = function() {};
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Constructs a new clone of this timer, while overriding its callback.
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function=} cb callback for new timer.
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {!Countdown} new clone.
395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdown.prototype.clone = function(cb) {};
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Constructs a new timer.  The timer has a very limited resolution, and does
445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * not attempt to be millisecond accurate. Its intended use is as a
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * low-precision timer that pauses while debugging.
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number=} timeoutMillis how long, in milliseconds, the countdown
475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     lasts.
485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function=} cb called back when the countdown expires.
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {Countdown}
515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction CountdownTimer(timeoutMillis, cb) {
535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.remainingMillis = 0;
545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.setTimeout(timeoutMillis || 0, cb);
555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/** Timer interval */
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.TIMER_INTERVAL_MILLIS = 200;
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Sets a new timeout for this timer. Only possible if the timer is not
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * currently active.
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts.
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function=} cb called back when the countdown expires.
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} whether the timeout could be set.
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.setTimeout = function(timeoutMillis, cb) {
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.timeoutId)
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!timeoutMillis || timeoutMillis < 0)
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.remainingMillis = timeoutMillis;
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.cb = cb;
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.remainingMillis > CountdownTimer.TIMER_INTERVAL_MILLIS) {
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.timeoutId =
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        window.setInterval(this.timerTick.bind(this),
775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu            CountdownTimer.TIMER_INTERVAL_MILLIS);
785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Set a one-shot timer for the last interval.
805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.timeoutId =
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        window.setTimeout(this.timerTick.bind(this), this.remainingMillis);
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return true;
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Clears this timer's timeout. */
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.clearTimeout = function() {
885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.timeoutId) {
895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    window.clearTimeout(this.timeoutId);
905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.timeoutId = undefined;
915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {number} how many milliseconds are remaining until the timer expires.
965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.millisecondsUntilExpired = function() {
985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return this.remainingMillis > 0 ? this.remainingMillis : 0;
995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** @return {boolean} whether the timer has expired. */
1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.expired = function() {
1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return this.remainingMillis <= 0;
1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Constructs a new clone of this timer, while overriding its callback.
1085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function=} cb callback for new timer.
1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {!Countdown} new clone.
1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.clone = function(cb) {
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return new CountdownTimer(this.remainingMillis, cb);
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Timer callback. */
1165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuCountdownTimer.prototype.timerTick = function() {
1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.remainingMillis -= CountdownTimer.TIMER_INTERVAL_MILLIS;
1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.expired()) {
1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    window.clearTimeout(this.timeoutId);
1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.timeoutId = undefined;
1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (this.cb) {
1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.cb();
1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
126