OSDN Git Service

Fix the bug of X.NodeAnime.
[pettanr/clientJs.git] / 0.6.x / js / 01_core / 12_XClosure.js
1 var \r
2         X_CLOSURE_LIVE_LIST     = [],\r
3 \r
4         X_CLOSURE_POOL_LIST     = [],\r
5 \r
6         X_Closure_COMMAND_BACK  = X_CLOSURE_LIVE_LIST,\r
7 \r
8         X_Closure_COMMAND_DROP  = X_CLOSURE_POOL_LIST,\r
9         \r
10         X_CLOSURE_THIS_FUNC     = 1,\r
11         X_CLOSURE_HANDLEEVENT   = 2,\r
12         X_CLOSURE_FUNC_ONLY     = 3,\r
13         X_CLOSURE_THIS_FUNCNAME = 4;\r
14 \r
15 /**\r
16  * <p>クロージャに関するポリシーと再利用可能クロージャについて次の記事をご覧ください。\r
17  *      <a href="http://outcloud.blogspot.jp/2015/05/reusable-closure.html" target="_blank">再利用できるクロージャを使ったWebアプリケーション開発</a>\r
18  * \r
19  * <h5>再利用可能クロージャの作成</h5>\r
20  * X_Closure_create() で再利用可能なクロージャの作成。次のパターンで呼び出します。<br>\r
21  * 最大で三つの引数を並べる一連のパターンは、 EventDispatcher.listen unlisten, listening や X.Timer.add, once でも使われますので、ここでよく目を通しておきます。\r
22  * \r
23  * <h5>再利用可能クロージャの破棄と再利用</h5>\r
24  * X_Closure_correct() によってクロージャは回収され再利用に備えます。<br>\r
25  * 実は、クロージャが束縛するのは、this コンテキストやコールバック関数といった、<strong>そのもの</strong>ではなく、それらを一定のルールで格納したハッシュです。<br>\r
26  * このハッシュはクロージャに与えた後も、適宜に取得が可能です。このハッシュのメンバーを書き換えることで、クロージャの this コンテキストやコールバック関数を書き換えています。\r
27  * \r
28  * @class __CallbackHash__\r
29  * @classdesc コールバック関数に this コンテキストや、追加の引数を設定するための情報を収めたハッシュです。<br>\r
30  * フレームワークユーザは直接触ることにはないが、重要な情報なので書いておきます。\r
31  * @private\r
32  */\r
33 var __CallbackHash__ =\r
34 /** @lends __CallbackHash__.prototype */\r
35 {\r
36         /**\r
37          * コールバックの種類を表す数値。 this + function, this.handleEvent, function only がある。\r
38          * @type {number} \r
39          */\r
40         cbKind : X_CLOSURE_THIS_FUNC,\r
41         /**\r
42          * コールバック。\r
43          * @type {funciton|undefined} \r
44          */\r
45         func : undefined,\r
46         /**\r
47          * コールバック名。コールバック作成時に関数が無い、関数が入れ替わっていても動作する。\r
48          * @type {string|undefined} \r
49          */\r
50         funcName : undefined,\r
51         /**\r
52          * コールバックの this コンテキスト。 \r
53          * @type {object|undefined}\r
54          */\r
55         context : undefined,\r
56         /**\r
57          * コールバックに追加する引数。イベントのコールバックでは event オブジェクトのあとに追加されるため supplement[0] が第一引数にならない点に注意。\r
58          * @type {Array|undefined}\r
59          */\r
60         supplement : undefined,\r
61         /**\r
62          * __CallbackHash__ の情報を元に、コールバックを実施するプロキシ。\r
63          * @type {Function}\r
64          */\r
65         proxy : X_Closure_proxyCallback\r
66 };\r
67 \r
68 // ------------------------------------------------------------------------- //\r
69 // --- implements ---------------------------------------------------------- //\r
70 // ------------------------------------------------------------------------- //\r
71 \r
72 function X_Closure_create( thisObject, opt_callback, opt_args /* [ listener || ( context + function ) || function ][ args... ] */ ){\r
73         var obj = X_Closure_classifyCallbackArgs( thisObject, opt_callback, opt_args ),\r
74                 l, ret, _obj;\r
75         \r
76         if( !obj.cbKind ) return obj;\r
77         \r
78         if( l = X_CLOSURE_POOL_LIST.length ){\r
79                 ret  = X_CLOSURE_POOL_LIST[ l - 1 ]; --X_CLOSURE_POOL_LIST.length; // ret = X_CLOSURE_POOL_LIST.pop();\r
80                 _obj = ret( X_Closure_COMMAND_BACK );\r
81                 \r
82                 _obj.cbKind       = obj.cbKind;\r
83                 _obj.funcName       = obj.funcName;\r
84                 _obj.func       = obj.func;\r
85                 _obj.context    = obj.context;\r
86                 _obj.supplement = obj.supplement;\r
87                 _obj.proxy      = X_Closure_proxyCallback;\r
88         } else {\r
89                 ret             = X_Closure_actualClosure( obj );\r
90                 obj.proxy       = X_Closure_proxyCallback;\r
91         };\r
92         X_CLOSURE_LIVE_LIST[ X_CLOSURE_LIVE_LIST.length ] = ret;\r
93         return ret;\r
94 };\r
95 \r
96 \r
97 function X_Closure_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){\r
98         var obj;\r
99         \r
100         if( X_Type_isObject( arg1 ) && X_Type_isFunction( arg2 ) ){\r
101                 obj  = { context : arg1, func : arg2, cbKind : X_CLOSURE_THIS_FUNC };\r
102         } else\r
103         if( X_Type_isObject( arg1 ) ){\r
104                 if( arg2 && X_Type_isString( arg2 ) ){\r
105                         obj  = { context : arg1, funcName : arg2, cbKind : X_CLOSURE_THIS_FUNCNAME };\r
106                 } else {\r
107                         obj  = { context : arg1, cbKind : X_CLOSURE_HANDLEEVENT };\r
108                         arg3 = arg2;                    \r
109                 };\r
110         } else\r
111         if( X_Type_isFunction( arg1 ) ){\r
112                 arg3 = arg2;\r
113                 if( alt_context ){\r
114                         obj  = { context : alt_context, func : arg1, cbKind : X_CLOSURE_THIS_FUNC };\r
115                 } else {\r
116                         obj  = { func : arg1, cbKind : X_CLOSURE_FUNC_ONLY };\r
117                 };\r
118         } else\r
119         if( X_Type_isFunction( arg2 ) ){\r
120                 //console.log( 'X_Closure_classifyCallbackArgs : arg1 が ' + arg1 + 'です' ); ie4 で error\r
121                 if( alt_context ){\r
122                         obj  = { context : alt_context, func : arg2, cbKind : X_CLOSURE_THIS_FUNC };\r
123                 } else {\r
124                         obj  = { func : arg2, cbKind : X_CLOSURE_FUNC_ONLY };\r
125                 };\r
126         } else\r
127         if( alt_context && X_Type_isString( arg1 ) ){\r
128                 arg3 = arg2;\r
129                 obj  = { context : alt_context, funcName : arg1, cbKind : X_CLOSURE_THIS_FUNCNAME };\r
130         } else\r
131         if( alt_context ){\r
132                 obj  = { context : alt_context, cbKind : X_CLOSURE_HANDLEEVENT };\r
133                 arg3 = arg1;\r
134         } else {\r
135                 console.log( '不正 ' + arg1 );\r
136                 console.dir( arg1 );\r
137                 return;\r
138         };\r
139         \r
140         if( X_Type_isArray( arg3 )){\r
141                 obj.supplement = arg3;\r
142         };\r
143         return ( obj.context || obj.supplement ) ? obj : arg1;\r
144 };\r
145 \r
146 function X_Closure_actualClosure( obj ){\r
147         return function(){\r
148                 if( arguments[ 0 ] === X_Closure_COMMAND_BACK ) return obj;\r
149                 if( arguments[ 0 ] !== X_Closure_COMMAND_DROP ) return obj.proxy && obj.proxy( obj, arguments );\r
150         };\r
151 };\r
152 \r
153 function X_Closure_proxyCallback( xfunc, _args ){\r
154         var args    = _args || [],\r
155                 thisObj = xfunc.context,\r
156                 func    = xfunc.func,\r
157                 supp    = xfunc.supplement,\r
158                 temp, ret, funcName;    \r
159         \r
160         if( supp && supp.length ){\r
161                 temp = [];\r
162                 args.length &&\r
163                         (\r
164                                 args.length === 1 ?\r
165                                         ( temp[ 0 ] = args[ 0 ] ) :\r
166                                         temp.push.apply( temp, args )\r
167                         );\r
168                 supp.length === 1 ?\r
169                         ( temp[ temp.length ] = supp[ 0 ] ) :\r
170                         temp.push.apply( temp, supp );\r
171                 args = temp;\r
172         };\r
173         \r
174         switch( xfunc.cbKind ){\r
175 \r
176                 case X_CLOSURE_THIS_FUNC :\r
177                         return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );\r
178                 \r
179                 case X_CLOSURE_THIS_FUNCNAME :\r
180                         funcName = xfunc.funcName;\r
181                 case X_CLOSURE_HANDLEEVENT :\r
182                         funcName = funcName || 'handleEvent';\r
183                         temp = thisObj[ funcName ];\r
184                         if( X_Type_isFunction( temp ) ){\r
185                                 return args.length === 0 ? thisObj[ funcName ]() :\r
186                                            args.length === 1 ? thisObj[ funcName ]( args[ 0 ] ) : temp.apply( thisObj, args );\r
187                         };\r
188                         break;\r
189                         /*\r
190                         if( temp !== func && X_Type_isFunction( temp ) ){\r
191                                 return args.length === 0 ? thisObj[ 'handleEvent' ]() : temp.apply( thisObj, args );\r
192                         } else\r
193                         if( X_Type_isFunction( thisObj ) ){\r
194                                 return args.length === 0 ? thisObj.call( thisObj ) : thisObj.apply( thisObj, args );\r
195                         };\r
196                         return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );*/\r
197                                                 \r
198                 case X_CLOSURE_FUNC_ONLY :\r
199                         return args.length === 0 ?\r
200                                         func() :\r
201                                 args.length === 1 ?\r
202                                         func( args[ 0 ] ) :\r
203                                         func.apply( null, args );\r
204         };\r
205         return X_CALLBACK_NONE;\r
206 };\r
207 \r
208 function X_Closure_correct( f ){\r
209         var i = X_CLOSURE_LIVE_LIST.indexOf( f ),\r
210                 obj;\r
211 \r
212         if( i !== -1 ){\r
213                 X_CLOSURE_LIVE_LIST.splice( i, 1 );\r
214                 X_CLOSURE_POOL_LIST[ X_CLOSURE_POOL_LIST.length ] = f;\r
215                 obj = f( X_Closure_COMMAND_BACK );\r
216                 X_Object_clear( obj );\r
217                 return true;\r
218         };\r
219 };\r
220 \r
221 function X_Closure_monitor(){\r
222         return {\r
223                 'Callback:Live' : X_CLOSURE_LIVE_LIST.length,\r
224                 'Callback:Pool' : X_CLOSURE_POOL_LIST.length\r
225         };\r
226 };\r
227 function X_Closure_gc(){\r
228         X_CLOSURE_POOL_LIST.length = 0; // ?\r
229 };\r
230 \r
231 X_TEMP.onSystemReady.push( function( sys ){\r
232         sys.monitor( X_Closure_monitor );\r
233         sys.gc( X_Closure_gc );\r
234 });\r
235 \r
236 \r