2 // ------------------------------------------------------------------------- //
\r
3 // ------------ local variables -------------------------------------------- //
\r
4 // ------------------------------------------------------------------------- //
\r
7 * 全てのクラスのスーパークラスのようなもの。(ライブラリ内にカプセル化されているため、ユーザが触ることはありません)<br>
\r
8 * X_Class.create() で定義されたクラスのインスタンスが共通で備えるメソッド を確認してください。
\r
9 * @class __ClassBase__
\r
21 X_Class_CLASS_LIST = [],
\r
22 X_Class_DEF_LIST = [],
\r
23 X_Class_PRIVATE_CLASS_LIST = [],
\r
24 X_Class_PRIVATE_DEF_LIST = [],
\r
25 X_Class_CALLING_SUPER = [],
\r
26 X_Class_CALL_SUPER_STACK = [],
\r
27 X_Class_killPrivateFlag = false,
\r
28 X_Class_traits = null,
\r
29 X_Class_useObjectCreate = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf
\r
30 X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && !!X_emptyFunction.prototype.__proto__,
\r
31 // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を使わないと動作,,,
\r
33 X_Class_CommonMethods =
\r
34 /** @lends __ClassBase__.prototype */
\r
37 * 全ての動的メンバを削除して、インスタンスを破棄する。<br>
\r
38 * インスタンスが X.EventDispatcher とそのサブクラスの場合、次の動作をする。
\r
40 * <li>X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X_Callback_PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。
\r
41 * <li>破棄に進む場合は、X.Event.KILL_INSTANCE を発火する。
\r
42 * <li>dispatch 中は、インスタンスの全ての dispatch が終了するまで実際の破棄を待つ。
\r
43 * <li>実際の破棄では、インスタンスのメンバの削除に加えて全てのイベントリスナを解除する。
\r
45 // TODO kill したインスタンスのイベントが残っていないか?これは開発用のみ
\r
46 'kill' : function(){
\r
47 var instance = this,
\r
48 klass = X_Class_getClass( instance ),
\r
49 def = X_Class_getClassDef( klass ),
\r
51 if( def.isPrivate && !X_Class_killPrivateFlag && ( !this[ '_listeners' ] || !this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] ) ){
\r
52 X.Logger.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );
\r
55 X_Class_killPrivateFlag = false; // instance.kill() 内で PrivateInstance.kill() を防ぐため
\r
57 // TODO kill 中の kill の呼び出しを防ぐ, 破棄済のインスタンスへの kill
\r
59 if( this[ 'instanceOf' ]( X_EventDispatcher ) ){
\r
60 if( !def.isPrivate ){
\r
61 if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){
\r
62 this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );
\r
65 if( this[ '_listeners' ] && this[ '_listeners' ][ X_Listeners_.DISPATCHING ] ){
\r
66 this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;
\r
70 this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE );
\r
73 // asyncDispatch の削除
\r
74 for( p in X_EventDispatcher_LAZY_TIMERS ){
\r
75 if( X_EventDispatcher_LAZY_TIMERS[ p ] === this ){
\r
76 delete X_EventDispatcher_LAZY_TIMERS[ p ]; // 削除が先!理由は X.Timer.removeを確認。
\r
77 X_Timer_remove( parseFloat( p ) );
\r
81 this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE );
\r
82 this[ '_listeners' ] && X_EventDispatcher_unlistenAll( this );
\r
85 for( p in instance ){
\r
86 if( instance.hasOwnProperty && !instance.hasOwnProperty( p ) ) continue;
\r
87 delete instance[ p ];
\r
90 def.live && def.live.splice( def.live.indexOf( instance ), 1 );
\r
91 def.pool[ def.pool.length ] = instance;
\r
93 if( def.privateClass ){
\r
94 i = def.userList.indexOf( instance );
\r
96 data = X_Class_getPrivate( instance );
\r
97 if( data[ '_listeners' ] && data[ '_listeners' ][ X_Listeners_.DISPATCHING ] && data[ 'instanceOf' ]( X.EventDispatcher ) ){
\r
98 data[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;
\r
100 X_Class_killPrivateFlag = true;
\r
103 def.dataList.splice( i, 1 );
\r
104 def.userList.splice( i, 1 );
\r
110 * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを探します。<br>
\r
111 * 内部的には、呼び出したコンストラクタは配列に控え(X_Class_CALLING_SUPER)、呼び出したコンストラクタ内でさらに Super が呼ばれた場合、配列を元にさらにスーパーなコンストラクタを辿ります。
\r
112 * @example Constructor : function( arg1, arg2 ){
\r
113 * this.Super( aeg1, arg2 );
\r
115 * @param var_args {...?} 親コンストラクタを呼ぶ際に渡す任意の数の引数
\r
118 // TODO 現在 new しているインスタンスを保持してチェックする
\r
119 'Super' : function( var_args ){
\r
121 i = X_Class_CALLING_SUPER.indexOf( sClass ),
\r
123 l, sList, def, sConst, ret;
\r
126 X_Class_CALLING_SUPER[ l = X_Class_CALLING_SUPER.length ] = sClass;
\r
127 X_Class_CALL_SUPER_STACK[ l ] = sList = [];
\r
129 sList = X_Class_CALL_SUPER_STACK[ i ];
\r
133 def = X_Class_getClassDef( sClass );
\r
134 sClass = def.SuperClass;
\r
135 sConst = def.SuperConstructor;
\r
136 if( sConst && sList[ ++n ] !== sConst ){
\r
137 sList[ n ] = sConst;
\r
138 ret = sConst.apply( this, arguments );
\r
140 if( !sList.length ){
\r
141 X_Class_CALLING_SUPER.splice( i, 1 );
\r
142 X_Class_CALL_SUPER_STACK.splice( i, 1 );
\r
147 console.log( 'スーパークラスのコンストラクタが見つかりません' );
\r
151 * func について、親クラスで設定されている同名の関数メンバーを呼び出す。<br>
\r
152 * 第一引数に関数を指定し、2つ以上の異なる名前で同じ関数がメンバーがいた場合、動作が不確実になります。<br>
\r
153 * 参考:<a href="http://qiita.com/no22@github/items/d3bead2acbb7ff1fb86b" target="_blank">ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し </a>
\r
154 * @param funcNameOrFunc {Function|string} スーパークラスの関数名 または、オーバーライド済の自身の関数。
\r
155 * @param var_args {...*} オーバーライド元関数に渡す任意の数の引数
\r
156 * @example return this.superCall( arguments.callee, param0, param1, ... );
\r
157 * @return {*} オーバーライド元の関数を呼び出した戻り値。
\r
159 'superCall' : function( funcNameOrFunc, var_args ){
\r
162 name, p, sFunc, hit = false;
\r
163 if( X_Type_isFunction( funcNameOrFunc ) ){
\r
164 for( p in this.constructor.prototype ){
\r
165 if( this.constructor.prototype[ p ] === funcNameOrFunc ){
\r
170 if( !name ) return;
\r
175 if( X_EMPTY_OBJECT[ name ] ) return;
\r
178 def = X_Class_getClassDef( sClass );
\r
179 sClass = def.SuperClass;
\r
180 sFunc = sClass.prototype[ name ];
\r
181 if( sFunc === funcNameOrFunc ){
\r
182 hit = true; // 現在の関数にヒット
\r
184 if( hit && X_Object_inObject( name, this ) ){
\r
185 if( X_Type_isFunction( sFunc ) ){
\r
186 switch( args.length ){
\r
188 return sFunc.call( this );
\r
190 return sFunc.call( this, args[ 1 ] );
\r
192 return sFunc.call( this, args[ 1 ], args[ 2 ] );
\r
194 return sFunc.call( this, args[ 1 ], args[ 2 ], args[ 3 ] );
\r
196 args = X_Object_cloneArray( args );
\r
198 return sFunc.apply( this, args );
\r
207 * インスタンスのクラスか?またはスーパークラスか?調べる。<br>
\r
208 * instanceof 構文をサポートしない環境(IE4,Mac IE5)を想定する場合、必ずこのメソッドを使用すること。<br>
\r
209 * クラスのインスタンスか?だけ調べたい場合は this.constructor === klass が高速。
\r
210 * @param klass {__ClassBase__} クラス定義
\r
211 * @return {boolean}
\r
213 // TODO instanceof に対応したブラウザはそちらを使用
\r
214 'instanceOf' : function( klass ){
\r
216 if( this.constructor === klass ) return true;
\r
217 while( Super = X_Class_getClassDef( Super ).SuperClass ){
\r
218 if( Super === klass ) return true;
\r
224 // ------------------------------------------------------------------------- //
\r
225 // --- interface ----------------------------------------------------------- //
\r
226 // ------------------------------------------------------------------------- //
\r
243 * <p>Class を定義し システムの管理下に置く。
\r
244 * <p>prototype 継承のブラウザ毎の差異も吸収し、 以下から最適な方法をしてくれる。
\r
247 * <li>Object.create はパフォーマンスが悪そうなので現在は使っていない。
\r
248 * <li>SubClass.prototype.__proto__ = SuperClass.prototype;
\r
249 * <li>SubClass.prototype = new SuperClass;
\r
253 * <li>X_Class.create( opt_settings, opt_name, opt_privateClass, opt_props ) でクラスを登録.
\r
254 * <li>コンストラクタ となるメソッドは、opt_props 内の Constructor : function( arg ){ ... }, に書く.
\r
255 * <li>通常通り new で インスタンス生成
\r
256 * <li>kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.
\r
257 * <li>pool が有効の場合、new で pool されたインスタンスが返される.
\r
259 * @namespace X.Class
\r
268 'NONE' : X_Class.NONE,
\r
271 * インスタンスは破棄時(this.kill())に回収され、次回の new MyClass() 時に再利用されます。
\r
274 'POOL_OBJECT' : X_Class.POOL_OBJECT,
\r
277 * 定義するクラスは抽象クラスになります。new AbstractClass() とするとエラーになります。
\r
280 'ABSTRACT' : X_Class.ABSTRACT,
\r
286 'FINAL' : X_Class.FINAL,
\r
289 * 使用を中止。petanR ライブラリ使用プロジェクトから SUPER_ACCESS を消したらここも削除。
\r
292 'SUPER_ACCESS' : X_Class.SUPER_ACCESS,
\r
295 * 内部コード、主に X.UI フレームワークに対して、フレーム外に露出するインスタンスとペアで動作する、シャドウなインスタンスの使用を宣言する。
\r
296 * Javascript はインスタンス毎のカプセル化がとてもコスト高。微妙なコスト増で隠蔽されたインスタンスを使う。
\r
299 'PRIVATE_DATA' : X_Class.PRIVATE_DATA,
\r
302 * 未実装。でも目印になるので付けておきましょう。
\r
305 'SINGLETON' : X_Class.SINGLETON,
\r
307 'create' : X_Class_create,
\r
311 '_newPrivate' : X_Class_newPrivate,
\r
313 '_getPrivate' : X_Class_getPrivate
\r
319 // ------------------------------------------------------------------------- //
\r
320 // --- implements ---------------------------------------------------------- //
\r
321 // ------------------------------------------------------------------------- //
\r
324 * X_Class.create() によるクラス定義は必ずしもコンストラクタを必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。
\r
325 * @alias X_Class.create
\r
326 * @param {string} [displayName] クラスの名前
\r
327 * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など
\r
328 * @param {__ClassBase__=} [privateClass] このクラスとペアで動作するシャドウクラス
\r
329 * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと
\r
330 * @return {__ClassBase__}
\r
332 function X_Class_create( /* displayName, classSetting, privateClass, props */ ){
\r
333 var args = X_Object_cloneArray( arguments ),
\r
334 displayName = args[ 0 ],
\r
336 opt_pool, opt_abstract, opt_final, opt_private,
\r
340 classDef = {}, hash;
\r
341 if( X_Type_isString( displayName ) === true ){
\r
342 classDef.displayName = displayName;
\r
347 classDef.setting = classSetting = args[ 0 ];
\r
348 if( X_Type_isNumber( classSetting ) ){
\r
349 opt_pool = !!( classSetting & X_Class.POOL_OBJECT );
\r
350 opt_abstract = !!( classSetting & X_Class.ABSTRACT );
\r
351 opt_final = !!( classSetting & X_Class.FINAL );
\r
352 opt_private = !!( classSetting & X_Class.PRIVATE_DATA );
\r
353 if( opt_final && opt_abstract ){
\r
354 X.Logger.critical( 'final & Abstract!' );
\r
359 classDef.setting = 0;
\r
363 if( X_Class_PRIVATE_CLASS_LIST.indexOf( args[ 0 ] ) !== -1 ){
\r
364 privateDef = X_Class_getClassDef( args[ 0 ] );
\r
365 if( privateDef.isPrivate !== true ){
\r
366 X.Logger.critical( 'PrivateClass not found! please, X_Class.create( X_Class.PRIVATE, {...} ).' );
\r
369 if( privateDef.Abstract === true ){
\r
370 X.Logger.critical( 'PrivateClass is Abstract!' );
\r
373 classDef.privateClass = args.shift();
\r
378 if( !X_Type_isObject( props ) ){
\r
379 // クラスメンバ用オブジェクトが無しでもクラスは作成可能
\r
382 if( props[ 'Constructor' ] && X_Type_isFunction( props[ 'Constructor' ] ) ){
\r
383 classDef.Constructor = props[ 'Constructor' ];
\r
386 klass = X_Callback_actualClosure( hash = { proxy : X_Class_actualConstructor } ); // TODO hash = classDef
\r
387 hash.klass = klass;
\r
388 klass[ 'superClassOf' ] = X_Class_superClassOf;
\r
389 klass[ 'subClassOf' ] = X_Class_subClassOf;
\r
391 if( X_Class_useObjectCreate ){
\r
392 klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonMethods, false );
\r
393 klass.prototype.constructor = klass;
\r
395 if( X_Class_use_proto_ ){
\r
396 X_Class_override( klass.prototype, props, true );
\r
397 if( X_Class_traits ){
\r
398 klass.prototype.__proto__ = X_Class_traits;
\r
400 X_Class_override( klass.prototype, X_Class_CommonMethods, false );
\r
403 klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonMethods, false );
\r
404 klass.prototype.constructor = klass;
\r
407 klass[ 'name' ] = displayName;
\r
409 if( opt_abstract ){
\r
410 classDef.Abstract = true;
\r
413 classDef.pool = [];
\r
414 if( opt_private === false ) classDef.live = [];
\r
417 classDef.Final = true;
\r
419 klass[ 'inherits' ] = X_Class_inherits;
\r
422 if( classDef.privateClass ){
\r
423 X.Logger.critical( 'Private Data Class has no PrivateClass!' );
\r
426 classDef.isPrivate = true;
\r
427 X_Class_PRIVATE_CLASS_LIST.push( klass );
\r
428 X_Class_PRIVATE_DEF_LIST.push( classDef );
\r
430 X_Class_CLASS_LIST.push( klass );
\r
431 X_Class_DEF_LIST.push( classDef );
\r
438 function X_Class_getClass( instance ){
\r
439 var cList = X_Class_CLASS_LIST,
\r
443 klass = cList[ --i ];
\r
444 if( instance.constructor === klass ) return klass;
\r
446 cList = X_Class_PRIVATE_CLASS_LIST;
\r
449 klass = cList[ --i ];
\r
450 if( instance.constructor === klass ) return klass;
\r
453 if( cList.indexOf( instance ) !== -1 ) return instance;
\r
454 if( X_Class_CLASS_LIST.indexOf( instance ) !== -1 ) return instance;
\r
457 function X_Class_getClassDef( KlassOrInstance ){
\r
458 var i = X_Class_CLASS_LIST.indexOf( KlassOrInstance );
\r
459 if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );
\r
460 if( i !== -1 ) return X_Class_DEF_LIST[ i ];
\r
462 i = X_Class_PRIVATE_CLASS_LIST.indexOf( KlassOrInstance );
\r
463 if( i === -1 ) i = X_Class_PRIVATE_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );
\r
464 if( i !== -1 ) return X_Class_PRIVATE_DEF_LIST[ i ];
\r
466 if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;
\r
467 if( X_Class_PRIVATE_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;
\r
470 function X_Class_newPrivate( /* instance, args */ ){
\r
471 var args = X_Object_cloneArray( arguments ),
\r
472 user = args.shift(),
\r
473 def = X_Class_getClassDef( user ),
\r
474 privateClass = def.privateClass,
\r
475 privateDef = X_Class_getClassDef( privateClass ),
\r
477 if( def.userList ){
\r
478 i = def.userList.indexOf( user );
\r
484 X.Logger.critical( 'PrivateData already exist!' );
\r
487 if( privateDef._tempUser ){
\r
488 X.Logger.critical( 'newPrivate を連続呼び出しされたところ破綻' );
\r
491 privateDef._tempUser = user;
\r
492 return X_Class_actualConstructor( privateClass( X_Closure_COMMAND_BACK ), args );// privateClass.__new( args );
\r
495 function X_Class_getPrivate( instance ){
\r
496 var def = X_Class_getClassDef( instance ),
\r
497 i = def.userList.indexOf( instance );
\r
498 if( i !== -1 ) return def.dataList[ i ];
\r
501 /* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */
\r
502 function X_Class_override( target, src, force ){
\r
505 if( p === 'Constructor' ) continue;
\r
506 if( p === '__proto__' || p === 'prototype' || p === 'constructor' ){
\r
507 X.Logger.critical( p + ' is reserved!' );
\r
510 if( force || target[ p ] === undefined ){
\r
511 target[ p ] = src[ p ];
\r
519 * @alias __ClassBase__.superClassOf
\r
520 * @param klass {__ClassBase__}
\r
521 * @return {boolean}
\r
523 function X_Class_superClassOf( klass ){
\r
524 var myDef = X_Class_getClassDef( this ),
\r
525 targetDef = X_Class_getClassDef( klass ),
\r
526 SuperClass = klass;
\r
528 if( !myDef || !targetDef || this === klass ) return false;
\r
530 while( SuperClass = X_Class_getClassDef( SuperClass ).SuperClass ){
\r
531 if( SuperClass === this ) return true;
\r
539 * @alias __ClassBase__.subClassOf
\r
541 * @param klass {__ClassBase__}
\r
542 * @return {boolean}
\r
544 function X_Class_subClassOf( klass ){
\r
545 return klass && X_Class_superClassOf.call( klass, this );
\r
549 * サブクラスを作ります。与える引数は X_Class.create と同じです。http://d.hatena.ne.jp/m-hiyama/20051018/1129605002
\r
550 * @alias __ClassBase__.inherits
\r
551 * @example var SubClass = SuperClass.inherits( 'Sub', X_Class.FINAL, { ... } );
\r
552 * @param {string} [displayName] クラスの名前
\r
553 * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など
\r
554 * @param {__ClassBase__=} [privateClass] このクラスとペアで動作するシャドウクラス
\r
555 * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと
\r
556 * @return {__ClassBase__}
\r
558 function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props */ ){
\r
559 var args = X_Object_cloneArray( arguments ),
\r
562 superDef = X_Class_getClassDef( Super ),
\r
563 displayName = args[ 0 ],
\r
567 if( superDef.Final ) X.Logger.critical( 'X.Class inherits, Class is final!' );
\r
570 if( X_Type_isString( displayName ) ){
\r
573 displayName = 'SubClass of ' + superDef.displayName;
\r
575 params.push( displayName );
\r
578 classSetting = args[ 0 ];
\r
579 if( X_Type_isNumber( classSetting ) ){
\r
582 // クラス設定がない場合、親からコピーして、Abstract flag は落とす??
\r
583 classSetting = superDef.setting;// &= ~X_Class.ABSTRACT;
\r
585 if( superDef.isPrivate ) classSetting = classSetting | X_Class.PRIVATE_DATA;
\r
587 params.push( classSetting );
\r
590 if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){
\r
591 params.push( args.shift() );
\r
593 if( superDef.privateClass ){
\r
594 params.push( superDef.privateClass );
\r
598 params.push( args[ 0 ] );
\r
601 if( X_Class_useObjectCreate ){
\r
602 X_Class_traits = Object.create( Super.prototype );
\r
604 if( X_Class_use_proto_ ){
\r
605 X_Class_traits = Super.prototype;
\r
607 X_Class_traits = new Super( X_Closure_COMMAND_DROP );
\r
609 klass = X_Class_create.apply( X.Class, params );
\r
610 X_Class_traits = null;
\r
612 def = X_Class_getClassDef( klass );
\r
614 def.SuperClass = Super;
\r
615 def.SuperProto = Super.prototype;
\r
616 def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor;
\r
622 * new の実体.コンストラクタの機能は instance.Constructor に書く.
\r
623 * これにより pool された オブジェクト(破棄されたインスタンス) を再利用できる
\r
625 function X_Class_actualConstructor( f, args ){
\r
626 var klass = f.klass,
\r
627 def = X_Class_getClassDef( klass ),
\r
628 dataUser = def._tempUser,
\r
631 if( def.Abstract ){
\r
632 X.Logger.critical( 'AbstractClass!' );
\r
635 if( def.isPrivate && !dataUser ){
\r
636 X.Logger.critical( 'use myClass.newPrivate( instance, ...args )!' );
\r
640 instance = def.pool && def.pool.length > 0 ?
\r
642 X_Class_useObjectCreate ?
\r
643 Object.create( klass.prototype ) :
\r
644 new klass( X_Closure_COMMAND_DROP );
\r
646 if( def.isPrivate ){
\r
647 userDef = X_Class_getClassDef( dataUser );
\r
648 userDef.dataList.push( instance );
\r
649 userDef.userList.push( dataUser );
\r
650 instance.User = dataUser;
\r
651 def._tempUser = null;
\r
653 def.live && def.live.push( instance );
\r
656 obj = def.Constructor ?
\r
657 def.Constructor.apply( instance, args ) :
\r
658 def.SuperConstructor &&
\r
659 def.SuperConstructor.apply( instance, args );
\r
660 if( ( X_Type_isObject( obj ) && obj !== instance ) || X_Type_isFunction( obj ) ){ // Class
\r
661 instance[ 'kill' ]();
\r
667 console.log( 'X.Core.Class' );
\r