Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / third_party / google_input_tools / third_party / closure_library / closure / goog / timer / timer.js
1 // Copyright 2006 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 timer class to which other classes and objects can
17  * listen on.  This is only an abstraction above setInterval.
18  *
19  * @see ../demos/timers.html
20  */
21
22 goog.provide('goog.Timer');
23
24 goog.require('goog.events.EventTarget');
25
26
27
28 /**
29  * Class for handling timing events.
30  *
31  * @param {number=} opt_interval Number of ms between ticks (Default: 1ms).
32  * @param {Object=} opt_timerObject  An object that has setTimeout, setInterval,
33  *     clearTimeout and clearInterval (eg Window).
34  * @constructor
35  * @extends {goog.events.EventTarget}
36  */
37 goog.Timer = function(opt_interval, opt_timerObject) {
38   goog.events.EventTarget.call(this);
39
40   /**
41    * Number of ms between ticks
42    * @type {number}
43    * @private
44    */
45   this.interval_ = opt_interval || 1;
46
47   /**
48    * An object that implements setTimeout, setInterval, clearTimeout and
49    * clearInterval. We default to the window object. Changing this on
50    * goog.Timer.prototype changes the object for all timer instances which can
51    * be useful if your environment has some other implementation of timers than
52    * the window object.
53    * @type {Object}
54    * @private
55    */
56   this.timerObject_ = opt_timerObject || goog.Timer.defaultTimerObject;
57
58   /**
59    * Cached tick_ bound to the object for later use in the timer.
60    * @type {Function}
61    * @private
62    */
63   this.boundTick_ = goog.bind(this.tick_, this);
64
65   /**
66    * Firefox browser often fires the timer event sooner
67    * (sometimes MUCH sooner) than the requested timeout. So we
68    * compare the time to when the event was last fired, and
69    * reschedule if appropriate. See also goog.Timer.intervalScale
70    * @type {number}
71    * @private
72    */
73   this.last_ = goog.now();
74 };
75 goog.inherits(goog.Timer, goog.events.EventTarget);
76
77
78 /**
79  * Maximum timeout value.
80  *
81  * Timeout values too big to fit into a signed 32-bit integer may cause
82  * overflow in FF, Safari, and Chrome, resulting in the timeout being
83  * scheduled immediately.  It makes more sense simply not to schedule these
84  * timeouts, since 24.8 days is beyond a reasonable expectation for the
85  * browser to stay open.
86  *
87  * @type {number}
88  * @private
89  */
90 goog.Timer.MAX_TIMEOUT_ = 2147483647;
91
92
93 /**
94  * Whether this timer is enabled
95  * @type {boolean}
96  */
97 goog.Timer.prototype.enabled = false;
98
99
100 /**
101  * An object that implements setTimout, setInterval, clearTimeout and
102  * clearInterval. We default to the global object. Changing
103  * goog.Timer.defaultTimerObject changes the object for all timer instances
104  * which can be useful if your environment has some other implementation of
105  * timers you'd like to use.
106  * @type {Object}
107  */
108 goog.Timer.defaultTimerObject = goog.global;
109
110
111 /**
112  * A variable that controls the timer error correction. If the
113  * timer is called before the requested interval times
114  * intervalScale, which often happens on mozilla, the timer is
115  * rescheduled. See also this.last_
116  * @type {number}
117  */
118 goog.Timer.intervalScale = 0.8;
119
120
121 /**
122  * Variable for storing the result of setInterval
123  * @type {?number}
124  * @private
125  */
126 goog.Timer.prototype.timer_ = null;
127
128
129 /**
130  * Gets the interval of the timer.
131  * @return {number} interval Number of ms between ticks.
132  */
133 goog.Timer.prototype.getInterval = function() {
134   return this.interval_;
135 };
136
137
138 /**
139  * Sets the interval of the timer.
140  * @param {number} interval Number of ms between ticks.
141  */
142 goog.Timer.prototype.setInterval = function(interval) {
143   this.interval_ = interval;
144   if (this.timer_ && this.enabled) {
145     // Stop and then start the timer to reset the interval.
146     this.stop();
147     this.start();
148   } else if (this.timer_) {
149     this.stop();
150   }
151 };
152
153
154 /**
155  * Callback for the setTimeout used by the timer
156  * @private
157  */
158 goog.Timer.prototype.tick_ = function() {
159   if (this.enabled) {
160     var elapsed = goog.now() - this.last_;
161     if (elapsed > 0 &&
162         elapsed < this.interval_ * goog.Timer.intervalScale) {
163       this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
164           this.interval_ - elapsed);
165       return;
166     }
167
168     // Prevents setInterval from registering a duplicate timeout when called
169     // in the timer event handler.
170     if (this.timer_) {
171       this.timerObject_.clearTimeout(this.timer_);
172       this.timer_ = null;
173     }
174
175     this.dispatchTick();
176     // The timer could be stopped in the timer event handler.
177     if (this.enabled) {
178       this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
179           this.interval_);
180       this.last_ = goog.now();
181     }
182   }
183 };
184
185
186 /**
187  * Dispatches the TICK event. This is its own method so subclasses can override.
188  */
189 goog.Timer.prototype.dispatchTick = function() {
190   this.dispatchEvent(goog.Timer.TICK);
191 };
192
193
194 /**
195  * Starts the timer.
196  */
197 goog.Timer.prototype.start = function() {
198   this.enabled = true;
199
200   // If there is no interval already registered, start it now
201   if (!this.timer_) {
202     // IMPORTANT!
203     // window.setInterval in FireFox has a bug - it fires based on
204     // absolute time, rather than on relative time. What this means
205     // is that if a computer is sleeping/hibernating for 24 hours
206     // and the timer interval was configured to fire every 1000ms,
207     // then after the PC wakes up the timer will fire, in rapid
208     // succession, 3600*24 times.
209     // This bug is described here and is already fixed, but it will
210     // take time to propagate, so for now I am switching this over
211     // to setTimeout logic.
212     //     https://bugzilla.mozilla.org/show_bug.cgi?id=376643
213     //
214     this.timer_ = this.timerObject_.setTimeout(this.boundTick_,
215         this.interval_);
216     this.last_ = goog.now();
217   }
218 };
219
220
221 /**
222  * Stops the timer.
223  */
224 goog.Timer.prototype.stop = function() {
225   this.enabled = false;
226   if (this.timer_) {
227     this.timerObject_.clearTimeout(this.timer_);
228     this.timer_ = null;
229   }
230 };
231
232
233 /** @override */
234 goog.Timer.prototype.disposeInternal = function() {
235   goog.Timer.superClass_.disposeInternal.call(this);
236   this.stop();
237   delete this.timerObject_;
238 };
239
240
241 /**
242  * Constant for the timer's event type
243  * @type {string}
244  */
245 goog.Timer.TICK = 'tick';
246
247
248 /**
249  * Calls the given function once, after the optional pause.
250  *
251  * The function is always called asynchronously, even if the delay is 0. This
252  * is a common trick to schedule a function to run after a batch of browser
253  * event processing.
254  *
255  * @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function
256  *     or object that has a handleEvent method.
257  * @param {number=} opt_delay Milliseconds to wait; default is 0.
258  * @param {SCOPE=} opt_handler Object in whose scope to call the listener.
259  * @return {number} A handle to the timer ID.
260  * @template SCOPE
261  */
262 goog.Timer.callOnce = function(listener, opt_delay, opt_handler) {
263   if (goog.isFunction(listener)) {
264     if (opt_handler) {
265       listener = goog.bind(listener, opt_handler);
266     }
267   } else if (listener && typeof listener.handleEvent == 'function') {
268     // using typeof to prevent strict js warning
269     listener = goog.bind(listener.handleEvent, listener);
270   } else {
271     throw Error('Invalid listener argument');
272   }
273
274   if (opt_delay > goog.Timer.MAX_TIMEOUT_) {
275     // Timeouts greater than MAX_INT return immediately due to integer
276     // overflow in many browsers.  Since MAX_INT is 24.8 days, just don't
277     // schedule anything at all.
278     return -1;
279   } else {
280     return goog.Timer.defaultTimerObject.setTimeout(
281         listener, opt_delay || 0);
282   }
283 };
284
285
286 /**
287  * Clears a timeout initiated by callOnce
288  * @param {?number} timerId a timer ID.
289  */
290 goog.Timer.clear = function(timerId) {
291   goog.Timer.defaultTimerObject.clearTimeout(timerId);
292 };