Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/async/animationdelay.js
3983 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview A delayed callback that pegs to the next animation frame
9
* instead of a user-configurable timeout.
10
*/
11
12
goog.provide('goog.async.AnimationDelay');
13
14
goog.require('goog.Disposable');
15
goog.require('goog.events');
16
goog.require('goog.functions');
17
18
19
20
// TODO(nicksantos): Should we factor out the common code between this and
21
// goog.async.Delay? I'm not sure if there's enough code for this to really
22
// make sense. Subclassing seems like the wrong approach for a variety of
23
// reasons. Maybe there should be a common interface?
24
25
26
27
/**
28
* A delayed callback that pegs to the next animation frame
29
* instead of a user configurable timeout. By design, this should have
30
* the same interface as goog.async.Delay.
31
*
32
* Uses requestAnimationFrame and friends when available, but falls
33
* back to a timeout of goog.async.AnimationDelay.TIMEOUT.
34
*
35
* For more on requestAnimationFrame and how you can use it to create smoother
36
* animations, see:
37
* @see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
38
*
39
* @param {function(this: THIS, number): void} listener Function to call
40
* when the delay completes. Will be passed the timestamp when it's called,
41
* in unix ms.
42
* @param {Window=} opt_window The window object to execute the delay in.
43
* Defaults to the global object.
44
* @param {THIS=} opt_handler The object scope to invoke the function in.
45
* @template THIS
46
* @constructor
47
* @struct
48
* @extends {goog.Disposable}
49
* @final
50
*/
51
goog.async.AnimationDelay = function(listener, opt_window, opt_handler) {
52
'use strict';
53
goog.async.AnimationDelay.base(this, 'constructor');
54
55
/**
56
* Identifier of the active delay timeout, or event listener,
57
* or null when inactive.
58
* @private {?goog.events.Key|number}
59
*/
60
this.id_ = null;
61
62
/**
63
* If we're using dom listeners.
64
* @private {?boolean}
65
*/
66
this.usingListeners_ = false;
67
68
/**
69
* The function that will be invoked after a delay.
70
* @const
71
* @private {function(this: THIS, number): void}
72
*/
73
this.listener_ = listener;
74
75
/**
76
* The object context to invoke the callback in.
77
* @const
78
* @private {(THIS|undefined)}
79
*/
80
this.handler_ = opt_handler;
81
82
/**
83
* @private {Window}
84
*/
85
this.win_ = opt_window || window;
86
87
/**
88
* Cached callback function invoked when the delay finishes.
89
* @private {function()}
90
*/
91
this.callback_ = goog.bind(this.doAction_, this);
92
};
93
goog.inherits(goog.async.AnimationDelay, goog.Disposable);
94
95
96
/**
97
* Default wait timeout for animations (in milliseconds). Only used for timed
98
* animation, which uses a timer (setTimeout) to schedule animation.
99
*
100
* @type {number}
101
* @const
102
*/
103
goog.async.AnimationDelay.TIMEOUT = 20;
104
105
106
/**
107
* Name of event received from the requestAnimationFrame in Firefox.
108
*
109
* @type {string}
110
* @const
111
* @private
112
*/
113
goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_ = 'MozBeforePaint';
114
115
116
/**
117
* Starts the delay timer. The provided listener function will be called
118
* before the next animation frame.
119
*/
120
goog.async.AnimationDelay.prototype.start = function() {
121
'use strict';
122
this.stop();
123
this.usingListeners_ = false;
124
125
var raf = this.getRaf_();
126
var cancelRaf = this.getCancelRaf_();
127
if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) {
128
// Because Firefox (Gecko) runs animation in separate threads, it also saves
129
// time by running the requestAnimationFrame callbacks in that same thread.
130
// Sadly this breaks the assumption of implicit thread-safety in JS, and can
131
// thus create thread-based inconsistencies on counters etc.
132
//
133
// Calling cycleAnimations_ using the MozBeforePaint event instead of as
134
// callback fixes this.
135
//
136
// Trigger this condition only if the mozRequestAnimationFrame is available,
137
// but not the W3C requestAnimationFrame function (as in draft) or the
138
// equivalent cancel functions.
139
this.id_ = goog.events.listen(
140
this.win_, goog.async.AnimationDelay.MOZ_BEFORE_PAINT_EVENT_,
141
this.callback_);
142
this.win_.mozRequestAnimationFrame(null);
143
this.usingListeners_ = true;
144
} else if (raf && cancelRaf) {
145
this.id_ = raf.call(this.win_, this.callback_);
146
} else {
147
this.id_ = this.win_.setTimeout(
148
// Prior to Firefox 13, Gecko passed a non-standard parameter
149
// to the callback that we want to ignore.
150
goog.functions.lock(this.callback_), goog.async.AnimationDelay.TIMEOUT);
151
}
152
};
153
154
155
/**
156
* Starts the delay timer if it's not already active.
157
*/
158
goog.async.AnimationDelay.prototype.startIfNotActive = function() {
159
'use strict';
160
if (!this.isActive()) {
161
this.start();
162
}
163
};
164
165
166
/**
167
* Stops the delay timer if it is active. No action is taken if the timer is not
168
* in use.
169
*/
170
goog.async.AnimationDelay.prototype.stop = function() {
171
'use strict';
172
if (this.isActive()) {
173
var raf = this.getRaf_();
174
var cancelRaf = this.getCancelRaf_();
175
if (raf && !cancelRaf && this.win_.mozRequestAnimationFrame) {
176
goog.events.unlistenByKey(this.id_);
177
} else if (raf && cancelRaf) {
178
cancelRaf.call(this.win_, /** @type {number} */ (this.id_));
179
} else {
180
this.win_.clearTimeout(/** @type {number} */ (this.id_));
181
}
182
}
183
this.id_ = null;
184
};
185
186
187
/**
188
* Fires delay's action even if timer has already gone off or has not been
189
* started yet; guarantees action firing. Stops the delay timer.
190
*/
191
goog.async.AnimationDelay.prototype.fire = function() {
192
'use strict';
193
this.stop();
194
this.doAction_();
195
};
196
197
198
/**
199
* Fires delay's action only if timer is currently active. Stops the delay
200
* timer.
201
*/
202
goog.async.AnimationDelay.prototype.fireIfActive = function() {
203
'use strict';
204
if (this.isActive()) {
205
this.fire();
206
}
207
};
208
209
210
/**
211
* @return {boolean} True if the delay is currently active, false otherwise.
212
*/
213
goog.async.AnimationDelay.prototype.isActive = function() {
214
'use strict';
215
return this.id_ != null;
216
};
217
218
219
/**
220
* Invokes the callback function after the delay successfully completes.
221
* @private
222
*/
223
goog.async.AnimationDelay.prototype.doAction_ = function() {
224
'use strict';
225
if (this.usingListeners_ && this.id_) {
226
goog.events.unlistenByKey(this.id_);
227
}
228
this.id_ = null;
229
230
// We are not using the timestamp returned by requestAnimationFrame
231
// because it may be either a Date.now-style time or a
232
// high-resolution time (depending on browser implementation). Using
233
// goog.now() will ensure that the timestamp used is consistent and
234
// compatible with goog.fx.Animation.
235
this.listener_.call(this.handler_, goog.now());
236
};
237
238
239
/** @override */
240
goog.async.AnimationDelay.prototype.disposeInternal = function() {
241
'use strict';
242
this.stop();
243
goog.async.AnimationDelay.base(this, 'disposeInternal');
244
};
245
246
247
/**
248
* @return {?function(function(number)): number} The requestAnimationFrame
249
* function, or null if not available on this browser.
250
* @private
251
*/
252
goog.async.AnimationDelay.prototype.getRaf_ = function() {
253
'use strict';
254
var win = this.win_;
255
return win.requestAnimationFrame || win.webkitRequestAnimationFrame ||
256
win.mozRequestAnimationFrame || win.oRequestAnimationFrame ||
257
win.msRequestAnimationFrame || null;
258
};
259
260
261
/**
262
* @return {?function(number): undefined} The cancelAnimationFrame function,
263
* or null if not available on this browser.
264
* @private
265
*/
266
goog.async.AnimationDelay.prototype.getCancelRaf_ = function() {
267
'use strict';
268
var win = this.win_;
269
return win.cancelAnimationFrame || win.cancelRequestAnimationFrame ||
270
win.webkitCancelRequestAnimationFrame ||
271
win.mozCancelRequestAnimationFrame || win.oCancelRequestAnimationFrame ||
272
win.msCancelRequestAnimationFrame || null;
273
};
274
275