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