summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resources/shared/js/cr/ui/repeating_button.js
blob: 35f235644bf777778539c1d08f128ce2cb316be5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// 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
  };
});