Javascript  |  184行  |  5.47 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('cr.ui', function() {
  /**
   * Creates a new button element. The repeating button behaves like a
   * keyboard button, which auto-repeats if held. This button is designed
   * for use with controls such as brightness and volume adjustment buttons.
   * @constructor
   * @extends {HTMLButtonElement}
   */
  var RepeatingButton = cr.ui.define('button');

  /**
   * DOM Events that may be fired by the Repeating button.
   */
  RepeatingButton.Event = {
    BUTTON_HELD: 'buttonHeld'
  };

  RepeatingButton.prototype = {
    __proto__: HTMLButtonElement.prototype,

    /**
     * Delay in milliseconds before the first repeat trigger of the button
     * held action.
     * @type {number}
     * @private
     */
    holdDelayTime_: 500,

    /**
     * Delay in milliseconds between triggers of the button held action.
     * @type {number}
     * @private
     */
    holdRepeatIntervalTime_: 50,

    /**
     * Callback function when repeated intervals trigger. Initialized when the
     * button is held for an initial delay period and cleared when the button
     * is released.
     * @type {function}
     * @private
     */
    intervalCallback_: undefined,

    /**
     * Callback function to arm the repeat timer. Initialized when the button
     * is pressed and cleared when the interval timer is set or the button is
     * released.
     * @type {function}
     * @private
     */
    armRepeaterCallback_: undefined,

    /**
     * Initializes the button.
     */
    decorate: function() {
      this.addEventListener('mousedown', this.buttonDown_.bind(this));
      this.addEventListener('mouseup', this.buttonUp_.bind(this));
      this.addEventListener('mouseout', this.buttonUp_.bind(this));
      this.addEventListener('touchstart', this.touchStart_.bind(this));
      this.addEventListener('touchend', this.buttonUp_.bind(this));
      this.addEventListener('touchcancel', this.buttonUp_.bind(this));
    },

    /**
     * Called when the user initiates a touch gesture.
     * @param {!Event} e The triggered event.
     * @private
     */
    touchStart_: function(e) {
      // Block system level gestures to prevent double tap to zoom. Also,
      // block following mouse event to prevent double firing of the button
      // held action in the case of a tap. Otherwise, a single tap action in
      // webkit generates the following event sequence: touchstart, touchend,
      // mouseover, mousemove, mousedown, mouseup and click.
      e.preventDefault();
      this.buttonDown_(e);
    },

    /**
     * Called when the user presses this button.
     * @param {!Event} e The triggered event.
     * @private
     */
    buttonDown_: function(e) {
      this.clearTimeout_();
      // Trigger the button held action immediately, after an initial delay and
      // then repeated based on a fixed time increment. The time intervals are
      // in agreement with the defaults for the ChromeOS keyboard and virtual
      // keyboard.
      // TODO(kevers): Consider adding a common location for picking up the
      //               initial delay and repeat interval.
      this.buttonHeld_();
      var self = this;
      this.armRepeaterCallback_ = function() {
        // In the event of a click/tap operation, this button has already been
        // released by the time this timeout triggers. Test to ensure that the
        // button is still being held (i.e. clearTimeout has not been called).
        if (self.armRepeaterCallback_) {
          self.armRepeaterCallback_ = undefined;
          self.buttonHeld_();
          self.intervalCallback_ = setInterval(self.buttonHeld_.bind(self),
                                               self.holdRepeatIntervalTime_);
        }
      };
      setTimeout(this.armRepeaterCallback_, this.holdDelayTime_);
    },

    /**
     * Called when the user releases this button.
     * @param {!Event} e The triggered event.
     * @private
     */
    buttonUp_: function(e) {
      this.clearTimeout_();
    },

    /**
     * Resets the interval callback.
     * @private
     */
    clearTimeout_: function() {
      if (this.armRepeaterCallback_) {
        clearTimeout(this.armRepeaterCallback_);
        this.armRepeaterCallback_ = undefined;
      }
      if (this.intervalCallback_) {
        clearInterval(this.intervalCallback_);
        this.intervalCallback_ = undefined;
      }
    },

    /**
     * Dispatches the action associated with keeping this button pressed.
     * @private
     */
    buttonHeld_: function() {
      cr.dispatchSimpleEvent(this, RepeatingButton.Event.BUTTON_HELD);
    },

    /**
     * Getter for the initial delay before repeating.
     * @type {number} The delay in milliseconds.
     */
    get repeatDelay() {
      return this.holdDelayTime_;
    },

    /**
     * Setter for the initial delay before repeating.
     * @type {number} The delay in milliseconds.
     */
    set repeatDelay(delay) {
      this.holdDelayTime_ = delay;
    },

    /**
     * Getter for the repeat interval.
     * @type {number} The repeat interval in milliseconds.
     */
    get repeatInterval() {
      return this.holdRepeatIntervalTime_;
    },

    /**
     * Setter for the repeat interval.
     * @type {number} The interval in milliseconds.
     */
   set repeatInterval(delay) {
     this.holdRepeatIntervalTime_ = delay;
   }
  };

  return {
    RepeatingButton: RepeatingButton
  };
});