OSDN Git Service

Version 0.6.90, performance fix for HTML5Audio & rename SilverLight -> Silverlight.
[pettanr/clientJs.git] / 0.6.x / js / 00_core / 05_XTimer.js
1 /**\r
2  * use X.Callback\r
3  */\r
4 \r
5 /*\r
6  * \r
7  * http://please-sleep.cou929.nu/script-yielding-with-setimmediate.html\r
8  * setImmediate での script yielding\r
9  * \r
10  * http://ie.microsoft.com/testdrive/Performance/setImmediateSorting/Default.html\r
11  * setImmediate API\r
12  * \r
13  * if( timer < 4ms ) useSetImmediate\r
14  * \r
15  *         if (window.msSetImmediate)\r
16         {\r
17             this.timer = msSetImmediate(function () { t.stepper(); });        \r
18         }\r
19         else if (window.MozSetImmediate)\r
20         {\r
21             this.timer = MozSetImmediate(function () { t.stepper(); });        \r
22         }\r
23         else if (window.WebkitSetImmediate) {\r
24             this.timer = WebkitSetImmediate(function () { t.stepper(); });\r
25         }\r
26         else if (window.OSetImmediate)\r
27         {\r
28             this.timer = OSetImmediate(function () { t.stepper(); });        \r
29         }\r
30  */\r
31 \r
32 \r
33 // ------------------------------------------------------------------------- //\r
34 // ------------ local variables -------------------------------------------- //\r
35 // ------------------------------------------------------------------------- //\r
36 \r
37 var X_Timer_SET_TIMEOUT   = window.setTimeout,\r
38         X_Timer_CLEAR_TIMEOUT = window.clearTimeout,\r
39         \r
40         X_Timer_REQ_ANIME_FRAME =\r
41                 window.requestAnimationFrame ||\r
42                 window.webkitRequestAnimationFrame ||\r
43                 window.mozRequestAnimationFrame ||\r
44                 window.oRequestAnimationFrame ||\r
45                 window.msRequestAnimationFrame ||\r
46                 false,\r
47         X_Timer_CANCEL_ANIME_FRAME =\r
48                 window.cancelRequestAnimationFrame ||\r
49                 window.webkitCancelAnimationFrame ||\r
50                 window.webkitCancelRequestAnimationFrame ||\r
51                 window.mozCancelRequestAnimationFrame ||\r
52                 window.oCancelRequestAnimationFrame ||\r
53                 window.msCancelRequestAnimationFrame ||\r
54                 false,\r
55                 \r
56         X_Timer_INTERVAL_TIME  = 16,\r
57         X_Timer_TICKET_LIST    = [],\r
58         X_Timer_removal        = null,\r
59         X_Timer_uid            = 0,\r
60         X_Timer_timerId        = 0,\r
61         X_Timer_next           = 0,\r
62         X_Timer_busyTimeout    = false, \r
63         X_Timer_endTime        = 0, // iOS\r
64         \r
65         X_Timer_REQ_FRAME_LIST = [],\r
66         X_Timer_requestID      = 0,\r
67         X_Timer_busyOnFrame    = false;\r
68 \r
69 // ------------------------------------------------------------------------- //\r
70 // --- interface ----------------------------------------------------------- //\r
71 // ------------------------------------------------------------------------- //\r
72 X.Timer = {\r
73         \r
74         add : function( time, opt_count, args1, args2, args3 ){\r
75                 var list = X_Timer_TICKET_LIST,\r
76                         hash, obj;\r
77                 time = time < X_Timer_INTERVAL_TIME ? 1 : time / X_Timer_INTERVAL_TIME | 0; // 正の数で使える「Math.floor(x)」を「(x | 0)」に;\r
78                 \r
79                 if( !X.Type.isNumber( opt_count ) ){\r
80                         args3 = args2;\r
81                         args2 = args1;\r
82                         args1 = opt_count;\r
83                         opt_count = 0;\r
84                 };\r
85                 \r
86                 hash = X.Callback._classifyCallbackArgs( args1, args2, args3 );\r
87                 if( !hash ) return -1; // dev only\r
88                 \r
89                 if( !hash.k ) hash = { f : hash };\r
90                 hash.time  = time;\r
91                 hash.last  = time;\r
92                 hash.count = opt_count;\r
93                 hash.uid   = ++X_Timer_uid;\r
94                 list[ list.length ] = hash;\r
95                 \r
96             !X_Timer_busyTimeout && X_Timer_update();\r
97             return X_Timer_uid;\r
98         },\r
99         \r
100         once : function( time, args1, args2, args3 ){\r
101                 return X.Timer.add( time, 1, args1, args2, args3 );\r
102         },\r
103         \r
104         remove : function( uid ){\r
105                 var list = X_Timer_TICKET_LIST,\r
106                         i    = list.length,\r
107                         l    = i,\r
108                         f, q;\r
109                 // fire 中の cancel\r
110                 if( X_Timer_busyTimeout ){\r
111                         if( !X_Timer_removal ) X_Timer_removal = {};\r
112                         X_Timer_removal[ uid ] = true;\r
113                 } else {\r
114                         for( ; i; ){\r
115         \r
116                                 if( ( q = list[ --i ] ).uid === uid ){\r
117                                         list.splice( i, 1 );\r
118                                         ( /* q[ INDEX_COUNT ] <= next  || */ l === 1 ) && X_Timer_update();\r
119                                         break;\r
120                                 };\r
121                         };                              \r
122                 };\r
123         },\r
124         \r
125         requestFrame : X_Timer_REQ_ANIME_FRAME ?\r
126                 (function( args1, args2, args3 ){\r
127                         var i = X_Timer_REQ_FRAME_LIST.length,\r
128                                 f;\r
129                         i === 0 && ( X_Timer_requestID = X_Timer_REQ_ANIME_FRAME( X_Timer_onEnterFrame ) );\r
130                         f = X_Timer_REQ_FRAME_LIST[ i ] = X.Callback._classifyCallbackArgs( args1, args2, args3 );\r
131                         return f.uid = ++X_Timer_uid;\r
132                 }) :\r
133                 (function( args1, args2, args3 ){\r
134                         var i = X_Timer_REQ_FRAME_LIST.length,\r
135                                 f;\r
136                         i === 0 && ( X_Timer_requestID = X.Timer.add( 0, 1, X_Timer_onEnterFrame ) );\r
137                         f = X_Timer_REQ_FRAME_LIST[ i ] = X.Callback._classifyCallbackArgs( args1, args2, args3 );\r
138                         return f.uid = ++X_Timer_uid;\r
139                 }),\r
140         \r
141         cancelFrame : X_Timer_CANCEL_ANIME_FRAME ?\r
142                 (function( uid ){\r
143                         var list = X_Timer_REQ_FRAME_LIST,\r
144                                 l    = list.length,\r
145                                 i    = l,\r
146                                 f;\r
147                         // fire 中の cancel\r
148                         if( X_Timer_busyOnFrame ){\r
149                                 if( !X_Timer_removal ) X_Timer_removal = {};\r
150                                 X_Timer_removal[ uid ] = true;\r
151                         } else {\r
152                                 for( ; i; ){\r
153                                         if( ( f = list[ --i ] ).uid < uid ) break;\r
154                                         if( f.uid === uid ){\r
155                                                 list.splice( i, 1 );\r
156                                                 l === 1 && X_Timer_CANCEL_ANIME_FRAME( X_Timer_requestID );\r
157                                                 break;\r
158                                         };\r
159                                 };                              \r
160                         };\r
161                 }) :\r
162                 (function( uid ){\r
163                         var list = X_Timer_REQ_FRAME_LIST,\r
164                                 l    = list.length,\r
165                                 i    = l,\r
166                                 f;\r
167                         // fire 中の cancel\r
168                         if( X_Timer_busyOnFrame ){\r
169                                 if( !X_Timer_removal ) X_Timer_removal = {};\r
170                                 X_Timer_removal[ uid ] = true;\r
171                         } else {\r
172                                 for( ; i; ){\r
173                                         if( ( f = list[ --i ] ).uid < uid ) break;\r
174                                         if( f.uid === uid ){\r
175                                                 list.splice( i, 1 );\r
176                                                 l === 1 && X.Timer.remove( X_Timer_requestID );\r
177                                                 break;\r
178                                         };\r
179                                 };\r
180                         };\r
181                 })\r
182         \r
183 };\r
184 \r
185 // ------------------------------------------------------------------------- //\r
186 // --- implements ---------------------------------------------------------- //\r
187 // ------------------------------------------------------------------------- //\r
188 \r
189 if( X.UA.IE4 || X.UA.MacIE ){\r
190         X.Timer[ '_' ] = X_Timer_onTimeout;\r
191         X_Timer_onTimeout = 'X.Timer._()';\r
192 };\r
193 \r
194 function X_Timer_onTimeout(){\r
195         var next  = X_Timer_next,\r
196                 list  = X_Timer_TICKET_LIST,\r
197                 i     = 0,\r
198                 l     = list.length,\r
199                 limit = X.getTime() + X_Timer_INTERVAL_TIME / 2,\r
200                 heavy,\r
201                 q, f, c, r, uid;\r
202         \r
203         if( X_Timer_busyTimeout ){\r
204                 alert( 'busy!' );\r
205         };\r
206         \r
207         X_Timer_busyTimeout = true;\r
208         \r
209     for( ; i < l; ++i ){\r
210         q = list[ i ];\r
211         if( X_Timer_removal && X_Timer_removal[ q.uid ] ) continue;\r
212                 if( 0 < ( q.last -= next ) ) continue;\r
213                 if( heavy ){\r
214                         if( q.last <= 0 ) q.last = 1;\r
215                         continue;\r
216                 };\r
217                 c = q.count;\r
218                 \r
219                 if( q.k ){\r
220                         q.a = [];\r
221                         r = X.Callback._proxyCallback( q );\r
222                 } else {\r
223                         r = q.f();\r
224                 };\r
225                 \r
226                 //console.log( 'fire....' );\r
227                 \r
228                 if( limit <= X.getTime() ){\r
229                         console.log( '******* heavy!' );\r
230                         // 関数の実行に時間がかかる場合、次のタイミングに\r
231                         heavy = true;\r
232                 };\r
233                 \r
234                 if( r & X.Callback.UN_LISTEN || c === 1 ){\r
235                         list.splice( i, 1 );\r
236                         --i;\r
237                         --l;\r
238                         continue;\r
239                 } else\r
240                 if( 1 < c ) --q.count;\r
241                 q.last = q.time;\r
242     };\r
243     X_Timer_timerId     = 0;\r
244     X_Timer_busyTimeout = false;\r
245     if( X_Timer_removal ){\r
246         for( uid in X_Timer_removal ){\r
247                 X.Timer.remove( X_Timer_removal[ uid ] );\r
248         };\r
249         X_Timer_removal = null;\r
250     };\r
251     X_Timer_update();\r
252 };\r
253 \r
254 function X_Timer_update(){\r
255         var list = X_Timer_TICKET_LIST,\r
256                 i    = list.length,\r
257                 n;\r
258         if( i === 0 ){\r
259                 X_Timer_timerId && X_Timer_CLEAR_TIMEOUT( X_Timer_timerId );\r
260                 X_Timer_timerId = 0;\r
261                 return;\r
262         };\r
263         \r
264         1 < i && list.sort( X_Timer_compareQueue );\r
265         \r
266     n = list[ i - 1 ].last;\r
267     \r
268     if( n < X_Timer_next || X_Timer_timerId === 0 ){\r
269         X_Timer_timerId && X_Timer_CLEAR_TIMEOUT( X_Timer_timerId );\r
270         X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, X_Timer_INTERVAL_TIME * n );\r
271         X_Timer_endTime = X.getTime() + X_Timer_INTERVAL_TIME * n; // iOS\r
272         X_Timer_next = n;\r
273     };\r
274 };\r
275 \r
276 // http://havelog.ayumusato.com/develop/javascript/e528-ios6_scrolling_timer_notcall.html\r
277 // iOS6 スクロール中のタイマー発火絡みのバグ備忘\r
278 if( X.UA.iOS ){\r
279         window.addEventListener( 'scroll', function(){\r
280                 var last;\r
281                 if( X_Timer_timerId ){\r
282                         window.clearTimeout( X_Timer_timerId );\r
283                         last = X_Timer_endTime - X.getTime();\r
284                         X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, 0 < last ? last : 0 );\r
285                 };\r
286         });\r
287 };\r
288 \r
289 \r
290 // ページを読み込んでからの時間\r
291 function X_Timer_onEnterFrame( time ){\r
292         var list = X_Timer_REQ_FRAME_LIST,\r
293                 l    = list.length,\r
294                 i    = 0, q, uid;\r
295 \r
296         time = time || X.getTime();\r
297         X_Timer_busyOnFrame = true;\r
298         // console.log( X.getTime() + ' , ' + time );\r
299     for( ; i < l; ++i ){\r
300         q = list[ i ];\r
301         \r
302         if( X_Timer_removal && X_Timer_removal[ q.uid ] ) continue;\r
303         \r
304                 if( q.k ){\r
305                         q.a = [ time ];\r
306                         X.Callback._proxyCallback( q );\r
307                 } else {\r
308                         q( time );\r
309                 };\r
310     };\r
311 \r
312     list.splice( 0, l );\r
313     if( list.length ){\r
314         X_Timer_requestID = X_Timer_REQ_ANIME_FRAME ? X_Timer_REQ_ANIME_FRAME( X_Timer_onEnterFrame ) : X.Timer.add( 0, 1, X_Timer_onEnterFrame );\r
315     };\r
316     \r
317     X_Timer_busyOnFrame = false;\r
318     if( X_Timer_removal ){\r
319         for( uid in X_Timer_removal ){\r
320                 X.Timer.cancelFrame( X_Timer_removal[ uid ] );\r
321         };\r
322         X_Timer_removal = null;\r
323     };\r
324 };\r
325 \r
326 // 大きい -> 小さい\r
327 function X_Timer_compareQueue( a, b ){\r
328         return a.last < b.last ? 1 : a.last === b.last ? 0 : -1;\r
329 };\r
330 \r
331 \r
332 console.log( 'X.Core.Timer' );\r