chromium/third_party/google-closure-library/closure/goog/async/delay.js

/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Defines a class useful for handling functions that must be
 * invoked after a delay, especially when that delay is frequently restarted.
 * Examples include delaying before displaying a tooltip, menu hysteresis,
 * idle timers, etc.
 * @see ../demos/timers.html
 */


goog.provide('goog.async.Delay');

goog.require('goog.Disposable');
goog.require('goog.Timer');



/**
 * A Delay object invokes the associated function after a specified delay. The
 * interval duration can be specified once in the constructor, or can be defined
 * each time the delay is started. Calling start on an active delay will reset
 * the timer.
 *
 * @param {function(this:THIS)} listener Function to call when the
 *     delay completes.
 * @param {number=} opt_interval The default length of the invocation delay (in
 *     milliseconds).
 * @param {THIS=} opt_handler The object scope to invoke the function in.
 * @template THIS
 * @constructor
 * @struct
 * @extends {goog.Disposable}
 * @final
 */
goog.async.Delay = function(listener, opt_interval, opt_handler) {
  'use strict';
  goog.async.Delay.base(this, 'constructor');

  /**
   * The function that will be invoked after a delay.
   * @private {function(this:THIS)}
   */
  this.listener_ = listener;

  /**
   * The default amount of time to delay before invoking the callback.
   * @type {number}
   * @private
   */
  this.interval_ = opt_interval || 0;

  /**
   * The object context to invoke the callback in.
   * @type {Object|undefined}
   * @private
   */
  this.handler_ = opt_handler;


  /**
   * Cached callback function invoked when the delay finishes.
   * @type {Function}
   * @private
   */
  this.callback_ = goog.bind(this.doAction_, this);
};
goog.inherits(goog.async.Delay, goog.Disposable);


/**
 * Identifier of the active delay timeout, or 0 when inactive.
 * @type {number}
 * @private
 */
goog.async.Delay.prototype.id_ = 0;


/**
 * Disposes of the object, cancelling the timeout if it is still outstanding and
 * removing all object references.
 * @override
 * @protected
 */
goog.async.Delay.prototype.disposeInternal = function() {
  'use strict';
  goog.async.Delay.base(this, 'disposeInternal');
  this.stop();
  delete this.listener_;
  delete this.handler_;
};


/**
 * Starts the delay timer. The provided listener function will be called after
 * the specified interval. Calling start on an active timer will reset the
 * delay interval.
 * @param {number=} opt_interval If specified, overrides the object's default
 *     interval with this one (in milliseconds).
 */
goog.async.Delay.prototype.start = function(opt_interval) {
  'use strict';
  this.stop();
  this.id_ = goog.Timer.callOnce(
      this.callback_,
      opt_interval !== undefined ? opt_interval : this.interval_);
};


/**
 * Starts the delay timer if it's not already active.
 * @param {number=} opt_interval If specified and the timer is not already
 *     active, overrides the object's default interval with this one (in
 *     milliseconds).
 */
goog.async.Delay.prototype.startIfNotActive = function(opt_interval) {
  'use strict';
  if (!this.isActive()) {
    this.start(opt_interval);
  }
};


/**
 * Stops the delay timer if it is active. No action is taken if the timer is not
 * in use.
 */
goog.async.Delay.prototype.stop = function() {
  'use strict';
  if (this.isActive()) {
    goog.Timer.clear(this.id_);
  }
  this.id_ = 0;
};


/**
 * Fires delay's action even if timer has already gone off or has not been
 * started yet; guarantees action firing. Stops the delay timer.
 */
goog.async.Delay.prototype.fire = function() {
  'use strict';
  this.stop();
  this.doAction_();
};


/**
 * Fires delay's action only if timer is currently active. Stops the delay
 * timer.
 */
goog.async.Delay.prototype.fireIfActive = function() {
  'use strict';
  if (this.isActive()) {
    this.fire();
  }
};


/**
 * @return {boolean} True if the delay is currently active, false otherwise.
 */
goog.async.Delay.prototype.isActive = function() {
  'use strict';
  return this.id_ != 0;
};


/**
 * Invokes the callback function after the delay successfully completes.
 * @private
 */
goog.async.Delay.prototype.doAction_ = function() {
  'use strict';
  this.id_ = 0;
  if (this.listener_) {
    this.listener_.call(this.handler_);
  }
};