|
1 var d3_timer_queue = null, |
|
2 d3_timer_interval, // is an interval (or frame) active? |
|
3 d3_timer_timeout; // is a timeout active? |
|
4 |
|
5 // The timer will continue to fire until callback returns true. |
|
6 d3.timer = function(callback, delay, then) { |
|
7 var found = false, |
|
8 t0, |
|
9 t1 = d3_timer_queue; |
|
10 |
|
11 if (arguments.length < 3) { |
|
12 if (arguments.length < 2) delay = 0; |
|
13 else if (!isFinite(delay)) return; |
|
14 then = Date.now(); |
|
15 } |
|
16 |
|
17 // See if the callback's already in the queue. |
|
18 while (t1) { |
|
19 if (t1.callback === callback) { |
|
20 t1.then = then; |
|
21 t1.delay = delay; |
|
22 found = true; |
|
23 break; |
|
24 } |
|
25 t0 = t1; |
|
26 t1 = t1.next; |
|
27 } |
|
28 |
|
29 // Otherwise, add the callback to the queue. |
|
30 if (!found) d3_timer_queue = { |
|
31 callback: callback, |
|
32 then: then, |
|
33 delay: delay, |
|
34 next: d3_timer_queue |
|
35 }; |
|
36 |
|
37 // Start animatin'! |
|
38 if (!d3_timer_interval) { |
|
39 d3_timer_timeout = clearTimeout(d3_timer_timeout); |
|
40 d3_timer_interval = 1; |
|
41 d3_timer_frame(d3_timer_step); |
|
42 } |
|
43 } |
|
44 |
|
45 function d3_timer_step() { |
|
46 var elapsed, |
|
47 now = Date.now(), |
|
48 t1 = d3_timer_queue; |
|
49 |
|
50 while (t1) { |
|
51 elapsed = now - t1.then; |
|
52 if (elapsed >= t1.delay) t1.flush = t1.callback(elapsed); |
|
53 t1 = t1.next; |
|
54 } |
|
55 |
|
56 var delay = d3_timer_flush() - now; |
|
57 if (delay > 24) { |
|
58 if (isFinite(delay)) { |
|
59 clearTimeout(d3_timer_timeout); |
|
60 d3_timer_timeout = setTimeout(d3_timer_step, delay); |
|
61 } |
|
62 d3_timer_interval = 0; |
|
63 } else { |
|
64 d3_timer_interval = 1; |
|
65 d3_timer_frame(d3_timer_step); |
|
66 } |
|
67 } |
|
68 |
|
69 d3.timer.flush = function() { |
|
70 var elapsed, |
|
71 now = Date.now(), |
|
72 t1 = d3_timer_queue; |
|
73 |
|
74 while (t1) { |
|
75 elapsed = now - t1.then; |
|
76 if (!t1.delay) t1.flush = t1.callback(elapsed); |
|
77 t1 = t1.next; |
|
78 } |
|
79 |
|
80 d3_timer_flush(); |
|
81 }; |
|
82 |
|
83 // Flush after callbacks, to avoid concurrent queue modification. |
|
84 function d3_timer_flush() { |
|
85 var t0 = null, |
|
86 t1 = d3_timer_queue, |
|
87 then = Infinity; |
|
88 while (t1) { |
|
89 if (t1.flush) { |
|
90 t1 = t0 ? t0.next = t1.next : d3_timer_queue = t1.next; |
|
91 } else { |
|
92 then = Math.min(then, t1.then + t1.delay); |
|
93 t1 = (t0 = t1).next; |
|
94 } |
|
95 } |
|
96 return then; |
|
97 } |
|
98 |
|
99 var d3_timer_frame = window.requestAnimationFrame |
|
100 || window.webkitRequestAnimationFrame |
|
101 || window.mozRequestAnimationFrame |
|
102 || window.oRequestAnimationFrame |
|
103 || window.msRequestAnimationFrame |
|
104 || function(callback) { setTimeout(callback, 17); }; |