1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/**
6 * @fileoverview Provides a countdown-based timer.
7 */
8'use strict';
9
10/**
11 * A countdown timer.
12 * @interface
13 */
14function Countdown() {}
15
16/**
17 * Sets a new timeout for this timer.
18 * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts.
19 * @param {Function=} cb called back when the countdown expires.
20 * @return {boolean} whether the timeout could be set.
21 */
22Countdown.prototype.setTimeout = function(timeoutMillis, cb) {};
23
24/** Clears this timer's timeout. */
25Countdown.prototype.clearTimeout = function() {};
26
27/**
28 * @return {number} how many milliseconds are remaining until the timer expires.
29 */
30Countdown.prototype.millisecondsUntilExpired = function() {};
31
32/** @return {boolean} whether the timer has expired. */
33Countdown.prototype.expired = function() {};
34
35/**
36 * Constructs a new clone of this timer, while overriding its callback.
37 * @param {Function=} cb callback for new timer.
38 * @return {!Countdown} new clone.
39 */
40Countdown.prototype.clone = function(cb) {};
41
42/**
43 * Constructs a new timer.  The timer has a very limited resolution, and does
44 * not attempt to be millisecond accurate. Its intended use is as a
45 * low-precision timer that pauses while debugging.
46 * @param {number=} timeoutMillis how long, in milliseconds, the countdown
47 *     lasts.
48 * @param {Function=} cb called back when the countdown expires.
49 * @constructor
50 * @implements {Countdown}
51 */
52function CountdownTimer(timeoutMillis, cb) {
53  this.remainingMillis = 0;
54  this.setTimeout(timeoutMillis || 0, cb);
55}
56
57/** Timer interval */
58CountdownTimer.TIMER_INTERVAL_MILLIS = 200;
59
60/**
61 * Sets a new timeout for this timer. Only possible if the timer is not
62 * currently active.
63 * @param {number} timeoutMillis how long, in milliseconds, the countdown lasts.
64 * @param {Function=} cb called back when the countdown expires.
65 * @return {boolean} whether the timeout could be set.
66 */
67CountdownTimer.prototype.setTimeout = function(timeoutMillis, cb) {
68  if (this.timeoutId)
69    return false;
70  if (!timeoutMillis || timeoutMillis < 0)
71    return false;
72  this.remainingMillis = timeoutMillis;
73  this.cb = cb;
74  if (this.remainingMillis > CountdownTimer.TIMER_INTERVAL_MILLIS) {
75    this.timeoutId =
76        window.setInterval(this.timerTick.bind(this),
77            CountdownTimer.TIMER_INTERVAL_MILLIS);
78  } else {
79    // Set a one-shot timer for the last interval.
80    this.timeoutId =
81        window.setTimeout(this.timerTick.bind(this), this.remainingMillis);
82  }
83  return true;
84};
85
86/** Clears this timer's timeout. */
87CountdownTimer.prototype.clearTimeout = function() {
88  if (this.timeoutId) {
89    window.clearTimeout(this.timeoutId);
90    this.timeoutId = undefined;
91  }
92};
93
94/**
95 * @return {number} how many milliseconds are remaining until the timer expires.
96 */
97CountdownTimer.prototype.millisecondsUntilExpired = function() {
98  return this.remainingMillis > 0 ? this.remainingMillis : 0;
99};
100
101/** @return {boolean} whether the timer has expired. */
102CountdownTimer.prototype.expired = function() {
103  return this.remainingMillis <= 0;
104};
105
106/**
107 * Constructs a new clone of this timer, while overriding its callback.
108 * @param {Function=} cb callback for new timer.
109 * @return {!Countdown} new clone.
110 */
111CountdownTimer.prototype.clone = function(cb) {
112  return new CountdownTimer(this.remainingMillis, cb);
113};
114
115/** Timer callback. */
116CountdownTimer.prototype.timerTick = function() {
117  this.remainingMillis -= CountdownTimer.TIMER_INTERVAL_MILLIS;
118  if (this.expired()) {
119    window.clearTimeout(this.timeoutId);
120    this.timeoutId = undefined;
121    if (this.cb) {
122      this.cb();
123    }
124  }
125};
126