OSDN Git Service

Version 0.6.90, performance fix for HTML5Audio & rename SilverLight -> Silverlight.
[pettanr/clientJs.git] / 0.6.x / js / 01_core / 05_XClass.js
1 /**\r
2  * Class を定義し システムの管理下に置く.\r
3  * 全てのクラスと pool が有効の場合インスタンスへの参照が保持される.\r
4  *  1. X.Class.create( opt_settings, opt_name, opt_privateClass, opt_props ) でクラスを登録.\r
5  *  2. コンストラクタ となるメソッドは、Constructor : function( arg ){ ... }, に書く.\r
6  *  3. 通常通り new で インスタンス生成\r
7  *  4. kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.\r
8  *  5. pool が有効の場合、new で pool されたインスタンスが返される.\r
9  *  6. \r
10  * \r
11  */\r
12 \r
13 // ------------------------------------------------------------------------- //\r
14 // ------------ local variables -------------------------------------------- //\r
15 // ------------------------------------------------------------------------- //\r
16 var X_Class_CLASS_LIST         = [],\r
17         X_Class_DEF_LIST           = [],\r
18         X_Class_PRIVATE_CLASS_LIST = [],\r
19         X_Class_PRIVATE_DEF_LIST   = [],\r
20         X_Class_CALLING_SUPER      = [],\r
21         X_Class_CALL_SUPER_STACK   = [],\r
22         X_Class_killPrivateFlag    = false,\r
23         X_Class_traits             = null,\r
24         X_Class_useObjectCreate    = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf\r
25         X_Class_use_proto_         = !!X.emptyFunction.prototype.__proto__;\r
26 \r
27 // ------------------------------------------------------------------------- //\r
28 // --- interface ----------------------------------------------------------- //\r
29 // ------------------------------------------------------------------------- //\r
30 X.Class = {\r
31         \r
32         POOL_OBJECT  :  1,\r
33         ABSTRACT     :  2,\r
34         FINAL        :  4,\r
35         SUPER_ACCESS :  8,\r
36         PRIVATE_DATA : 16,\r
37         \r
38         SINGLETON    : 32, // 未実装\r
39         \r
40         create : function( /* displayName, classSetting, opt_PrivateClass, props */ ){\r
41                 var args        = X.copyArray( arguments ),\r
42                         displayName = args[ 0 ],\r
43                         classSetting,\r
44                         opt_pool, opt_abstract, opt_final, opt_private,\r
45                         privateDef,\r
46                         props,\r
47                         klass,\r
48                         classDef = {};\r
49                 if( X.Type.isString( displayName ) === true ){\r
50                         classDef.displayName = displayName;\r
51                         args.shift();\r
52                 };\r
53                 \r
54                 // クラス設定\r
55                 classDef.setting = classSetting = args[ 0 ];\r
56                 if( X.Type.isNumber( classSetting ) ){\r
57                         opt_pool     = !!( classSetting & X.Class.POOL_OBJECT  );\r
58                         opt_abstract = !!( classSetting & X.Class.ABSTRACT     );\r
59                         opt_final    = !!( classSetting & X.Class.FINAL        );\r
60                         opt_private  = !!( classSetting & X.Class.PRIVATE_DATA );\r
61                         if( opt_final && opt_abstract ){\r
62                                 X.Notification.critical( 'final & Abstract!' );\r
63                                 return;\r
64                         };      \r
65                         args.shift();\r
66                 } else {\r
67                         classDef.setting = 0;\r
68                 };\r
69                 \r
70                 // シャドウクラス\r
71                 if( X_Class_PRIVATE_CLASS_LIST.indexOf( args[ 0 ] ) !== -1 ){\r
72                         privateDef = X_Class_getClassDef( args[ 0 ] );\r
73                         if( privateDef.isPrivate !== true ){\r
74                                 X.Notification.critical( 'PrivateClass not found! please, X.Class.create( X.Class.PRIVATE, {...} ).' );\r
75                                 return;\r
76                         } else\r
77                         if( privateDef.Abstract === true ){\r
78                                 X.Notification.critical( 'PrivateClass is Abstract!' );\r
79                                 return;\r
80                         };\r
81                         classDef.privateClass = args.shift();\r
82                 };\r
83                 \r
84                 // インスタンスのメンバー\r
85                 props = args[ 0 ];\r
86                 if( !X.Type.isObject( props ) ){\r
87                         // サブクラスの場合、クラス定義の上書きがなくても作成可能\r
88                         // サブクラスでなくても、クラスメンバ用オブジェクトが無しでも作成可能\r
89                         //if( !X_Class_traits ){\r
90                         //      X.Notification.critical( 'No Class Def!' );\r
91                         //      return;\r
92                         //};\r
93                         props = {};\r
94                 } else\r
95                 if( props.Constructor && X.Type.isFunction( props.Constructor ) ){\r
96                         classDef.Constructor = props.Constructor;\r
97                 };\r
98 \r
99                 // TODO use X.Function\r
100                 klass = new Function( 'var a=arguments,f=a.callee;if(f.__new)return f.__new(a)' );\r
101                 klass.__new = X_Class_actualConstructor;\r
102                 klass.superClassOf = X_Class_superClassOf;\r
103                 klass.subClassOf   = X_Class_subClassOf;\r
104                 \r
105                 if( X_Class_useObjectCreate ){\r
106                         klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonProps, false );\r
107                         klass.prototype.constructor = klass;\r
108                 } else\r
109                 if( X_Class_use_proto_ ){\r
110                         X_Class_override( klass.prototype, props, true );\r
111                         if( X_Class_traits ){\r
112                                 klass.prototype.__proto__ = X_Class_traits;\r
113                         } else {\r
114                                 X_Class_override( klass.prototype, X_Class_CommonProps, false );\r
115                         };\r
116                 } else {\r
117                         klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonProps, false );\r
118                         klass.prototype.constructor = klass;\r
119                 };\r
120                 klass.name = displayName;\r
121                 \r
122                 if( opt_abstract === true ){\r
123                         classDef.Abstract = true;\r
124                 } else\r
125                 if( opt_pool === true ){\r
126                         classDef.pool = [];\r
127                         if( opt_private === false ) classDef.live = [];\r
128                 };                      \r
129                 if( opt_final === true ){\r
130                         classDef.Final = true;\r
131                 } else {\r
132                         klass.inherits = X_Class_inherits;\r
133                 };                      \r
134                 if( opt_private === true ){\r
135                         if( classDef.privateClass ){\r
136                                 X.Notification.critical( 'Private Data Class has no PrivateClass!' );\r
137                                 return;\r
138                         };\r
139                         classDef.isPrivate = true;\r
140                         X_Class_PRIVATE_CLASS_LIST.push( klass );\r
141                         X_Class_PRIVATE_DEF_LIST.push( classDef );\r
142                 } else {\r
143                         X_Class_CLASS_LIST.push( klass );\r
144                         X_Class_DEF_LIST.push( classDef );                              \r
145                 };\r
146                 return klass;\r
147         },\r
148         \r
149         _getClass    : X_Class_getClass,\r
150         \r
151         _getClassDef : X_Class_getClassDef,\r
152         \r
153         _newPrivate  : X_Class_newPrivate,\r
154         \r
155         _getPrivate  : X_Class_getPrivate,\r
156         \r
157         _override    : X_Class_override\r
158         \r
159 };\r
160 \r
161 // ------------------------------------------------------------------------- //\r
162 // --- implements ---------------------------------------------------------- //\r
163 // ------------------------------------------------------------------------- //\r
164 function X_Class_getClass( instance ){\r
165         var cList    = X_Class_CLASS_LIST,\r
166                 i        = cList.length,\r
167                 klass;\r
168         for( ; i; ){\r
169                 klass = cList[ --i ];\r
170                 if( instance.constructor === klass ) return klass;\r
171         };\r
172         cList = X_Class_PRIVATE_CLASS_LIST;\r
173         i     = cList.length;\r
174         for( ; i; ){\r
175                 klass = cList[ --i ];\r
176                 if( instance.constructor === klass ) return klass;\r
177         };\r
178         \r
179         if( cList.indexOf( instance ) !== -1 ) return instance;\r
180         if( X_Class_CLASS_LIST.indexOf( instance ) !== -1 ) return instance;\r
181 };\r
182 \r
183 function X_Class_getClassDef( KlassOrInstance ){\r
184         var i = X_Class_CLASS_LIST.indexOf( KlassOrInstance );\r
185         if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
186         if( i !== -1 ) return X_Class_DEF_LIST[ i ];\r
187         \r
188         i = X_Class_PRIVATE_CLASS_LIST.indexOf( KlassOrInstance );\r
189         if( i === -1 ) i = X_Class_PRIVATE_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
190         if( i !== -1 ) return X_Class_PRIVATE_DEF_LIST[ i ];\r
191         \r
192         if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
193         if( X_Class_PRIVATE_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
194 };\r
195 \r
196 function X_Class_newPrivate( /* instance, args */ ){\r
197         var args         = X.copyArray( arguments ),\r
198                 user         = args.shift(),\r
199                 def          = X_Class_getClassDef( user ),\r
200                 privateClass = def.privateClass,\r
201                 privateDef   = X_Class_getClassDef( privateClass ),\r
202                 i            = -1;\r
203         if( def.userList ){\r
204                 i = def.userList.indexOf( user );\r
205         } else {\r
206                 def.userList = [];\r
207                 def.dataList = [];\r
208         };\r
209         if( i !== -1 ){\r
210                 X.Notification.critical( 'PrivateData already exist!' );\r
211                 return;\r
212         };\r
213         if( privateDef._tempUser ){\r
214                 X.Notification.critical( 'newPrivate を連続呼び出しされたところ破綻' );\r
215                 return;\r
216         };\r
217         privateDef._tempUser = user;\r
218         return privateClass.__new( args );\r
219 };\r
220 \r
221 function X_Class_getPrivate( instance ){\r
222         var def = X_Class_getClassDef( instance ),\r
223                 i   = def.userList.indexOf( instance );\r
224         if( i !== -1 ) return def.dataList[ i ];\r
225 };\r
226 \r
227 /* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */\r
228 function X_Class_override( target, src, force ){\r
229         var p;\r
230         for( p in src ){\r
231                 if( p === 'Super' || p === 'SuperConstructor' || p === '__proto__' || p === 'prototype' || p === 'constructor' ){\r
232                         X.Notification.critical( 'Super & SuperConstructor is reserved!' );\r
233                         return;\r
234                 };\r
235                 if( force || target[ p ] === void 0 ){\r
236                         target[ p ] = src[ p ];\r
237                 };\r
238         };\r
239         return target;\r
240 };\r
241 \r
242 function X_Class_superClassOf( klass ){\r
243         var myDef      = X_Class_getClassDef( this ),\r
244                 targetDef  = X_Class_getClassDef( klass ),\r
245                 SuperClass = klass;\r
246 \r
247         if( !myDef || !targetDef || this === klass ) return false;\r
248         \r
249         while( SuperClass = X_Class_getClassDef( SuperClass ).SuperClass ){\r
250                 if( SuperClass === this ) return true;\r
251         };\r
252         \r
253         return false;\r
254 };\r
255 \r
256 function X_Class_subClassOf( klass ){\r
257         return X_Class_superClassOf.call( klass, this );\r
258 };\r
259         \r
260 /* サブクラスを作るメソッド  \r
261  * var subClass = superClass.inherits( ... ) \r
262  * http://d.hatena.ne.jp/m-hiyama/20051018/1129605002\r
263  */\r
264 function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props */ ){\r
265         var args        = X.copyArray( arguments ),\r
266                 params      = [],\r
267                 Super       = this,\r
268                 superDef    = X_Class_getClassDef( Super ),\r
269                 displayName = args[ 0 ],\r
270                 classSetting,\r
271                 opt_super,\r
272                 klass, def;\r
273         if( superDef.Final ) X.Notification.critical( 'X.Class inherits, Class is final!' );\r
274         \r
275         // サブクラス名\r
276         if( X.Type.isString( displayName ) ){\r
277                 args.shift();\r
278         } else {\r
279                 displayName = 'SubClass of ' + superDef.displayName;\r
280         };\r
281         params.push( displayName );\r
282         \r
283         // サブクラス設定\r
284         classSetting = args[ 0 ];\r
285         if( X.Type.isNumber( classSetting ) ){\r
286                 args.shift();\r
287         } else {\r
288                 // クラス設定がない場合、親からコピーして、Abstract flag は落とす??\r
289                 classSetting = superDef.setting;// &= ~X.Class.ABSTRACT;\r
290         };\r
291         if( superDef.isPrivate ) classSetting = classSetting | X.Class.PRIVATE_DATA;\r
292         opt_super = !!( classSetting & X.Class.SUPER_ACCESS );\r
293         params.push( classSetting );\r
294 \r
295         // サブクラスのシャドウ\r
296         if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){\r
297                 params.push( args.shift() );\r
298         } else\r
299         if( superDef.privateClass ){\r
300                 params.push( superDef.privateClass );\r
301         };\r
302         params.push( args[ 0 ] ); /* props サブクラスでは未定義でも可 */\r
303         \r
304         // 継承クラスの作成\r
305         if( X_Class_useObjectCreate ){\r
306                 X_Class_traits = Object.create( Super.prototype );\r
307         } else\r
308         if( X_Class_use_proto_ ){\r
309                 X_Class_traits = Super.prototype;\r
310         } else {\r
311                 Super.__new = null;\r
312                 X_Class_traits = new Super();\r
313                 Super.__new = X_Class_actualConstructor;                        \r
314         };\r
315         klass  = X.Class.create.apply( X.Class, params );\r
316         X_Class_traits = null;\r
317         \r
318         def    = X_Class_getClassDef( klass );\r
319         // 継承用プロパティを控える\r
320         if( opt_super === true ){\r
321                 def.superAccess = true;\r
322                 def.SuperClass  = Super;\r
323                 def.SuperProto  = Super.prototype;\r
324                 def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor;\r
325         } else {\r
326                 def.SuperClass = Super; // instanceOf() で親クラスを調べる!\r
327         };\r
328         \r
329         return klass;\r
330 };\r
331 \r
332 /* X.Class.create で作られたクラスのインスタンスが共通で備えるメソッド \r
333  *\r
334  *\r
335  */\r
336 var X_Class_CommonProps = {\r
337         kill : function(){\r
338                 var instance = this,\r
339                         klass    = X_Class_getClass( instance ),\r
340                         def      = X_Class_getClassDef( klass ),\r
341                         data, p, i;\r
342                 if( def.isPrivate && !X_Class_killPrivateFlag ){\r
343                         X.Notification.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );\r
344                         return;\r
345                 };\r
346                 X_Class_killPrivateFlag = false; // onKill 内で PrivateInstance.kill() を防ぐため\r
347                 \r
348                 // onKill() === false の場合、kill のキャンセル\r
349                 // private      は false での キャンセル は無視される\r
350                 \r
351                 if( this.instanceOf( X.EventDispatcher ) ){\r
352                         console.log( 'this.instanceOf( X.EventDispatcher )! ' + this._dispatching );\r
353                         if( !def.isPrivate ){\r
354                                 if( this._dispatching ){\r
355                                         this.dispatch( X.Event.BEFORE_KILL_INSTANCE );\r
356                                         this._killReserved = true;\r
357                                         this.dispatch( X.Event.KILL_INSTANCE_CANCELED );\r
358                                         return;\r
359                                 } else\r
360                                 if( this.dispatch( X.Event.BEFORE_KILL_INSTANCE ) & X.Callback.PREVENT_DEFAULT ){\r
361                                         this.dispatch( X.Event.KILL_INSTANCE_CANCELED );\r
362                                         return;\r
363                                 };\r
364                         } else {\r
365                                 this.dispatch( X.Event.BEFORE_KILL_INSTANCE );  \r
366                         };\r
367                         this.dispatch( X.Event.KILL_INSTANCE );\r
368                         this._listeners && this.unlisten();\r
369                 } else\r
370                 if( X.Type.isFunction( instance.onKill ) && instance.onKill() === false && !def.isPrivate ){\r
371                         return;\r
372                 };\r
373                 \r
374                 console.log('kill ' + this._dispatching);\r
375                 \r
376                 for( p in instance ){\r
377                         if( instance.hasOwnProperty && !instance.hasOwnProperty( p ) ) continue;\r
378                         delete instance[ p ];\r
379                 };\r
380                 if( def.pool ){\r
381                         def.live && def.live.splice( def.live.indexOf( instance ), 1 );\r
382                         def.pool[ def.pool.length ] = instance;\r
383                 };\r
384                 if( def.privateClass ){\r
385                         i = def.userList.indexOf( instance );\r
386                         if( i !== -1 ){\r
387                                 data            = X_Class_getPrivate( instance );\r
388                                 X_Class_killPrivateFlag = true;\r
389                                 if( data._dispatching && data.instanceOf( X.EventDispatcher ) ){\r
390                                         data._killReserved = true;\r
391                                 } else {\r
392                                         data.kill();\r
393                                 };\r
394                                 def.dataList.splice( i, 1 );\r
395                                 def.userList.splice( i, 1 );\r
396                         };\r
397                 };\r
398         },\r
399         \r
400         // TODO Super\r
401         // superCall\r
402         \r
403         instanceOf : function( klass ){\r
404                 var Super = this;\r
405                 if( this.constructor === klass ) return true;\r
406                 while( Super = X_Class_getClassDef( Super ).SuperClass ){\r
407                         if( Super === klass ) return true;\r
408                 };\r
409                 return false;\r
410         }\r
411 };\r
412         \r
413 /*\r
414  * new の実体.コンストラクタの機能は instance.Constructor に書く.\r
415  * これにより pool された オブジェクト(破棄されたインスタンス) を再利用できる\r
416  */\r
417 function X_Class_actualConstructor( args ){\r
418         var klass    = this,\r
419                 def      = X_Class_getClassDef( klass ),\r
420                 dataUser = def._tempUser,\r
421                 instance, obj,\r
422                 userDef;\r
423         if( def.Abstract ){\r
424                 X.Notification.critical( 'AbstractClass!' );\r
425                 return;\r
426         };\r
427         if( def.isPrivate && !dataUser ){\r
428                 X.Notification.critical( 'use myClass.newPrivate( instance, ...args )!' );\r
429                 return;\r
430         };\r
431         klass.__new = null;\r
432         instance = def.pool && def.pool.length > 0 ?\r
433                                         def.pool.pop() :\r
434                                 X_Class_useObjectCreate ?\r
435                                         Object.create( klass.prototype ) :\r
436                                         new klass();\r
437         klass.__new = X_Class_actualConstructor;\r
438         \r
439         if( def.isPrivate ){\r
440                 userDef = X_Class_getClassDef( dataUser );\r
441                 userDef.dataList.push( instance );\r
442                 userDef.userList.push( dataUser );\r
443                 instance.User = dataUser;\r
444                 def._tempUser = null;\r
445         } else {\r
446                 def.live && def.live.push( instance );\r
447         };\r
448         if( def.superAccess ){\r
449                 // TODO klass.prototype に移動\r
450                 instance.Super = def.SuperProto;\r
451                 instance.SuperConstructor = X_Class_superConstructor;\r
452         };\r
453         obj = def.Constructor ?\r
454                         def.Constructor.apply( instance, args ) :\r
455                 def.SuperConstructor &&\r
456                         def.SuperConstructor.apply( instance, args );\r
457         if( ( X.Type.isObject( obj ) && obj !== instance ) || X.Type.isFunction( obj ) ){ // Class\r
458                 instance.kill();\r
459                 return obj;\r
460         };\r
461         return instance;\r
462 };\r
463 /*  クラス定義を辿ってスーパークラスのコンストラクタを探す。\r
464  *  呼び出したコンストラクタは配列に控える。\r
465  *  さらに呼ばれた場合、配列を元にさらに奥のコンストラクタを取得\r
466  *  TODO 現在 new しているインスタンスを保持してチェックする\r
467  */\r
468 function X_Class_superConstructor(){\r
469         var sClass = this,\r
470                 i      = X_Class_CALLING_SUPER.indexOf( sClass ),\r
471                 n      = -1,\r
472                 l, sList, def, sConst, ret;\r
473 \r
474         if( i === -1 ){\r
475                 X_Class_CALLING_SUPER[ l = X_Class_CALLING_SUPER.length ] = sClass;\r
476                 X_Class_CALL_SUPER_STACK[ l ] = sList = [];\r
477         } else {\r
478                 sList = X_Class_CALL_SUPER_STACK[ i ];\r
479         };\r
480         \r
481         while( sClass ){\r
482                 def    = X_Class_getClassDef( sClass );\r
483                 sClass = def.SuperClass;\r
484                 sConst = def.SuperConstructor;\r
485                 if( sConst && sList[ ++n ] !== sConst ){\r
486                         sList[ n ] = sConst;\r
487                         ret = sConst.apply( this, arguments );\r
488                         --sList.length;\r
489                         if( !sList.length ){\r
490                                 X_Class_CALLING_SUPER.splice( i, 1 );\r
491                                 X_Class_CALL_SUPER_STACK.splice( i, 1 );\r
492                         };\r
493                         return ret;\r
494                 };\r
495         };\r
496 };\r
497 \r
498 console.log( 'X.Core.Class' );\r