OSDN Git Service

Version 0.6.160, fix X.Net.
[pettanr/clientJs.git] / 0.6.x / js / 01_core / 10_XCallback.js
1 \r
2 // ------------------------------------------------------------------------- //\r
3 // ------------ local variables -------------------------------------------- //\r
4 // ------------------------------------------------------------------------- //\r
5 \r
6 var \r
7         X_Callback_LIVE_LIST        = [],\r
8 \r
9         X_Callback_POOL_LIST        = [],\r
10 \r
11         X_Closure_COMMAND_BACK      = X_Callback_LIVE_LIST,\r
12 \r
13         X_Closure_COMMAND_DROP      = X_Callback_POOL_LIST,\r
14         \r
15         /** @const */\r
16         X_Callback_THIS_FUNC        = 1,\r
17         /** @const */\r
18         X_Callback_HANDLEEVENT      = 2,\r
19         /** @const */\r
20         X_Callback_FUNC_ONLY        = 3,\r
21         /** @const */\r
22         X_Callback_THIS_FUNCNAME    = 4,\r
23 \r
24         /** @const */\r
25         X_Callback_NONE             =  0,\r
26         /** @const */\r
27         X_Callback_UN_LISTEN        =  1,\r
28         /** @const */\r
29         X_Callback_STOP_PROPAGATION =  2,\r
30         /** @const */\r
31         X_Callback_STOP_NOW         =  4 | 2,  // 同一階層のリスナーのキャンセル(上位へもキャンセル)\r
32         /** @const */\r
33         X_Callback_PREVENT_DEFAULT  =  8,  // 結果動作のキャンセル,\r
34         /** @const */\r
35         X_Callback_CAPTURE_POINTER  = 16,\r
36         /** @const */\r
37         X_Callback_RELEASE_POINTER  = 32,\r
38         \r
39         /** @const */\r
40         X_Callback_SYS_CANCEL       = 64 | 4 | 2;\r
41 \r
42 \r
43 \r
44 /*\r
45  * handleEvent という関数のメンバーを持つオブジェクト\r
46  * @typedef {{ handleEvent : function }}\r
47  */\r
48 var listener;\r
49 \r
50 /**\r
51  * <p>クロージャに関するポリシーと再利用可能クロージャについて次の記事をご覧ください。\r
52  *      <a href="http://outcloud.blogspot.jp/2015/05/reusable-closure.html" target="_blank">再利用できるクロージャを使ったWebアプリケーション開発</a>\r
53  * \r
54  * <h5>再利用可能クロージャの作成</h5>\r
55  * X_Callback_create() で再利用可能なクロージャの作成。次のパターンで呼び出します。<br>\r
56  * 最大で三つの引数を並べる一連のパターンは、 EventDispatcher.listen unlisten, listening や X.Timer.add, once でも使われますので、ここでよく目を通しておきます。\r
57  * \r
58  * <table>\r
59  * <tr><th>this コンテキスト+関数<td>X_Callback_create( thisObject, func )<td>func.call( thisObject );\r
60  * <tr><th>this コンテキスト+関数+追加引数<td>X_Callback_create( thisObject, func, [ arg1, ...args ] )<td>func.apply( thisObject, [ arg1, ...args ] );\r
61  * <tr><th>listener オブジェクト<td>X_Callback_create( listener )<td>listener.handleEvent(); コールバックに関数でなく handleEvent 関数をメンバに持つオブジェクトを渡すのは NN4 からある javascript のお約束です。\r
62  * <tr><th>listener オブジェクト+追加引数<td>X_Callback_create( listener, [ arg1, ...args ] )<td>listener.handleEvent.apply( listener, [ arg1, ...args ] );\r
63  * <tr><th>関数<td>X_Callback_create( func )<td>特別な操作は不要なので再利用可能クロージャは作られません。func をそのまま利用します。\r
64  * <tr><th>関数+引数<td>X_Callback_create( func, [ arg1, ...args ] )<td>func.apply( ?, [ arg1, ...args ] );\r
65  * </table>\r
66  * \r
67  * <h5>再利用可能クロージャの破棄と再利用</h5>\r
68  * X_Callback_correct() によってクロージャは回収され再利用に備えます。<br>\r
69  * 実は、クロージャが束縛するのは、this コンテキストやコールバック関数といった、<strong>そのもの</strong>ではなく、それらを一定のルールで格納したハッシュです。<br>\r
70  * このハッシュはクロージャに与えた後も、適宜に取得が可能です。このハッシュのメンバーを書き換えることで、クロージャの this コンテキストやコールバック関数を書き換えています。\r
71  * \r
72  * @class __CallbackHash__\r
73  * @classdesc コールバック関数に this コンテキストや、追加の引数を設定するための情報を収めたハッシュです。<br>\r
74  * フレームワークユーザは直接触ることにはないが、重要な情報なので書いておきます。\r
75  * @private\r
76  */\r
77 var __CallbackHash__ =\r
78 /** @lends __CallbackHash__.prototype */\r
79 {\r
80         /**\r
81          * コールバックの種類を表す数値。 this + function, this.handleEvent, function only がある。\r
82          * @type {number} \r
83          */\r
84         kind : X_Callback_THIS_FUNC,\r
85         /**\r
86          * コールバック。\r
87          * @type {funciton|undefined} \r
88          */\r
89         func : undefined,\r
90         /**\r
91          * コールバック名。コールバック作成時に関数が無い、関数が入れ替わっていても動作する。\r
92          * @type {string|undefined} \r
93          */\r
94         name : undefined,\r
95         /**\r
96          * コールバックの this コンテキスト。 \r
97          * @type {listener|object|undefined}\r
98          */\r
99         context : undefined,\r
100         /**\r
101          * コールバックに追加する引数。イベントのコールバックでは event オブジェクトのあとに追加されるため supplement[0] が第一引数にならない点に注意。\r
102          * @type {Array|undefined}\r
103          */\r
104         supplement : undefined,\r
105         /**\r
106          * __CallbackHash__ の情報を元に、コールバックを実施するプロキシ。\r
107          * @type {Function}\r
108          */\r
109         proxy : X_Callback_proxyCallback\r
110 };\r
111 \r
112 /**\r
113  * X.Timer と X.EventDispatcher からのコールバックの返り値を定義。\r
114  * @namespace X.Callback\r
115  */\r
116 X[ 'Callback' ] = {\r
117         /**\r
118          * このコールバックでは返り値による操作は無い。\r
119          * @alias X.Callback.NONE\r
120          */\r
121         'NONE'             : X_Callback_NONE,\r
122         /**\r
123          * X.Timer, X.EventDispatcher のコールバックでタイマーやイベントリスナの解除に使用。\r
124          * @alias X.Callback.UN_LISTEN\r
125          */\r
126         'UN_LISTEN'        : X_Callback_UN_LISTEN,\r
127         /**\r
128          * 上位階層へのイベント伝播のキャンセル。DOM イベントのコールバックの戻り値に指定すると e.stopPropagation() が呼ばれる。\r
129          * @alias X.Callback.STOP_PROPAGATION\r
130          */\r
131         'STOP_PROPAGATION' : X_Callback_STOP_PROPAGATION,\r
132         /**\r
133          * 以降のイベントのディスパッチを中断する。STOP_PROPAGATION との違いは、次に控えているコールバックもキャンセルされる点。但し system によって追加されたイベントはキャンセルされない。\r
134          * @alias X.Callback.STOP_NOW\r
135          */\r
136         'STOP_NOW'         : X_Callback_STOP_NOW,\r
137         /**\r
138          * DOM イベントのコールバックの戻り値に指定すると e.preventDefault() が呼ばれる。\r
139          * またフレームワーク内で定義されたデフォルト動作の回避にも使用される。\r
140          * @alias X.Callback.PREVENT_DEFAULT\r
141          */\r
142         'PREVENT_DEFAULT'  : X_Callback_PREVENT_DEFAULT,\r
143         /**\r
144          * X.UI の uinode でポインターイベントの戻り値に指定すると、以降のポインターベントを独占する。\r
145          * @alias X.Callback.CAPTURE_POINTER\r
146          */\r
147         'CAPTURE_POINTER'  : X_Callback_CAPTURE_POINTER,\r
148         /**\r
149          * X.UI の uinode でポインターイベントの戻り値に指定すると、以降のポインターベントを独占を解除する。\r
150          * @alias X.Callback.RELEASE_POINTER\r
151          */\r
152         'RELEASE_POINTER'  : X_Callback_RELEASE_POINTER\r
153 };\r
154 \r
155 // ------------------------------------------------------------------------- //\r
156 // --- implements ---------------------------------------------------------- //\r
157 // ------------------------------------------------------------------------- //\r
158 \r
159 function X_Callback_create( thisObject, opt_callback, opt_args /* [ listener || ( context + function ) || function ][ args... ] */ ){\r
160         var obj = X_Callback_classifyCallbackArgs( thisObject, opt_callback, opt_args ),\r
161                 l, ret, _obj;\r
162         \r
163         if( !obj.kind ) return obj;\r
164         \r
165         if( l = X_Callback_POOL_LIST.length ){\r
166                 ret  = X_Callback_POOL_LIST[ l - 1 ]; --X_Callback_POOL_LIST.length; // ret = X_Callback_POOL_LIST.pop();\r
167                 _obj = ret( X_Closure_COMMAND_BACK );\r
168                 \r
169                 _obj.kind       = obj.kind;\r
170                 _obj.name       = obj.name;\r
171                 _obj.func       = obj.func;\r
172                 _obj.context    = obj.context;\r
173                 _obj.supplement = obj.supplement;\r
174                 _obj.proxy      = X_Callback_proxyCallback;\r
175         } else {\r
176                 ret             = X_Callback_actualClosure( obj );\r
177                 obj.proxy       = X_Callback_proxyCallback;\r
178         };\r
179         X_Callback_LIVE_LIST[ X_Callback_LIVE_LIST.length ] = ret;\r
180         return ret;\r
181 };\r
182 \r
183 \r
184 function X_Callback_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){\r
185         var obj;\r
186         \r
187         if( X_Type_isObject( arg1 ) && X_Type_isFunction( arg2 ) ){\r
188                 obj  = { context : arg1, func : arg2, kind : X_Callback_THIS_FUNC };\r
189         } else\r
190         if( X_Type_isObject( arg1 ) ){\r
191                 if( arg2 && X_Type_isString( arg2 ) ){\r
192                         obj  = { context : arg1, name : arg2, kind : X_Callback_THIS_FUNCNAME };\r
193                 } else {\r
194                         obj  = { context : arg1, kind : X_Callback_HANDLEEVENT };\r
195                         arg3 = arg2;                    \r
196                 };\r
197         } else\r
198         if( X_Type_isFunction( arg1 ) ){\r
199                 arg3 = arg2;\r
200                 if( alt_context ){\r
201                         obj  = { context : alt_context, func : arg1, kind : X_Callback_THIS_FUNC };\r
202                 } else {\r
203                         obj  = { func : arg1, kind : X_Callback_FUNC_ONLY };\r
204                 };\r
205         } else\r
206         if( X_Type_isFunction( arg2 ) ){\r
207                 //console.log( 'X_Callback_classifyCallbackArgs : arg1 が ' + arg1 + 'です' ); ie4 で error\r
208                 if( alt_context ){\r
209                         obj  = { context : alt_context, func : arg2, kind : X_Callback_THIS_FUNC };\r
210                 } else {\r
211                         obj  = { func : arg2, kind : X_Callback_FUNC_ONLY };\r
212                 };\r
213         } else\r
214         if( alt_context && X_Type_isString( arg1 ) ){\r
215                 arg3 = arg2;\r
216                 obj  = { context : alt_context, name : arg1, kind : X_Callback_THIS_FUNCNAME };\r
217         } else\r
218         if( alt_context ){\r
219                 obj  = { context : alt_context, kind : X_Callback_HANDLEEVENT };\r
220                 arg3 = arg1;\r
221         } else {\r
222                 console.log( '不正 ' + arg1 );\r
223                 console.dir( arg1 );\r
224                 return;\r
225         };\r
226         \r
227         if( X_Type_isArray( arg3 )){\r
228                 obj.supplement = arg3;\r
229         };\r
230         return ( obj.context || obj.supplement ) ? obj : arg1;\r
231 };\r
232 \r
233 function X_Callback_actualClosure( obj ){\r
234         return function(){\r
235                 if( arguments[ 0 ] === X_Closure_COMMAND_BACK ) return obj;\r
236                 if( arguments[ 0 ] !== X_Closure_COMMAND_DROP ) return obj.proxy( obj, arguments );\r
237         };\r
238 };\r
239 \r
240 function X_Callback_proxyCallback( xfunc, _args ){\r
241         var args    = _args || [],\r
242                 thisObj = xfunc.context,\r
243                 func    = xfunc.func,\r
244                 supp    = xfunc.supplement,\r
245                 temp, ret, funcName;    \r
246         \r
247         if( supp && supp.length ){\r
248                 temp = [];\r
249                 args.length &&\r
250                         (\r
251                                 args.length === 1 ?\r
252                                         ( temp[ 0 ] = args[ 0 ] ) :\r
253                                         temp.push.apply( temp, args )\r
254                         );\r
255                 supp.length === 1 ?\r
256                         ( temp[ temp.length ] = supp[ 0 ] ) :\r
257                         temp.push.apply( temp, supp );\r
258                 args = temp;\r
259         };\r
260         \r
261         switch( xfunc.kind ){\r
262 \r
263                 case X_Callback_THIS_FUNC :\r
264                         return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );\r
265                 \r
266                 case X_Callback_THIS_FUNCNAME :\r
267                         funcName = xfunc.name;\r
268                 case X_Callback_HANDLEEVENT :\r
269                         funcName = funcName || 'handleEvent';\r
270                         temp = thisObj[ funcName ];\r
271                         if( X_Type_isFunction( temp ) ){\r
272                                 return args.length === 0 ? thisObj[ 'handleEvent' ]() :\r
273                                            args.length === 1 ? thisObj[ 'handleEvent' ]( args[ 0 ] ) : temp.apply( thisObj, args );\r
274                         };\r
275                         break;\r
276                         /*\r
277                         if( temp !== func && X_Type_isFunction( temp ) ){\r
278                                 return args.length === 0 ? thisObj[ 'handleEvent' ]() : temp.apply( thisObj, args );\r
279                         } else\r
280                         if( X_Type_isFunction( thisObj ) ){\r
281                                 return args.length === 0 ? thisObj.call( thisObj ) : thisObj.apply( thisObj, args );\r
282                         };\r
283                         return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );*/\r
284                                                 \r
285                 case X_Callback_FUNC_ONLY :\r
286                         return args.length === 0 ?\r
287                                         func() :\r
288                                 args.length === 1 ?\r
289                                         func( args[ 0 ] ) :\r
290                                         func.apply( null, args );\r
291         };\r
292         return X_Callback_NONE;\r
293 };\r
294 \r
295 function X_Callback_correct( f ){\r
296         var i = X_Callback_LIVE_LIST.indexOf( f ),\r
297                 obj;\r
298         if( i !== -1 ){\r
299                 X_Callback_LIVE_LIST.splice( i, 1 );\r
300                 X_Callback_POOL_LIST[ X_Callback_POOL_LIST.length ] = f;\r
301                 obj = f( X_Closure_COMMAND_BACK );\r
302                 delete obj.kind;\r
303                 if( obj.name ) delete obj.name;\r
304                 if( obj.func ) delete obj.func;\r
305                 if( obj.context ) delete obj.context;\r
306                 if( obj.supplement ) delete obj.supplement;\r
307                 delete obj.proxy;\r
308                 return true;\r
309         };\r
310         return false;\r
311 };\r
312 \r
313 function X_Callback_monitor(){\r
314         return {\r
315                 'Callback:Live' : X_Callback_LIVE_LIST.length,\r
316                 'Callback:Pool' : X_Callback_POOL_LIST.length\r
317         };\r
318 };\r
319 function X_Callback_gc(){\r
320         X_Callback_POOL_LIST.length = 0; // ?\r
321 };\r
322 \r
323 X_TEMP.onSystemReady.push( function( sys ){\r
324         sys.monitor( X_Callback_monitor );\r
325         sys.gc( X_Callback_gc );\r
326 });\r
327 \r
328 \r
329 console.log( 'X.Core.Callback' );\r
330 \r