OSDN Git Service

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