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_SUPER_CALLER = [],
\r
24 X_Class_SUPER_STACKS = [],
\r
25 X_Class_traits = null,
\r
26 X_Class_useObjectCreate = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf
\r
27 // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を辞めると動作,,,
\r
28 X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] &&
\r
29 // Android で原因不明のエラーに遭遇しているのは、この辺りが怪しい... 2016.3.9
\r
30 !X_UA[ 'AOSP' ] && !X_UA[ 'ChromeWV' ] &&
\r
31 !!X_emptyFunction.prototype.__proto__,
\r
32 X_Class_constructorFix = X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5,
\r
33 X_Class_SEAL_KILLING = [],
\r
35 X_Class_CommonMethods =
\r
36 /** @lends __ClassBase__.prototype */
\r
39 * 全ての動的メンバを削除して、インスタンスを破棄する。<br>
\r
40 * インスタンスが X.EventDispatcher とそのサブクラスの場合、次の動作をする。
\r
42 * <li>X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X.Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。この間に kill() が呼ばれても無視される。
\r
43 * <li>破棄に進む場合は、X.Event.KILL_INSTANCE を発火する。
\r
44 * <li>dispatch 中は、インスタンスの全ての dispatch が終了するまで実際の破棄を待つ。
\r
45 * <li>実際の破棄では、インスタンスのメンバの削除に加えて全てのイベントリスナを解除する。
\r
47 // TODO kill したインスタンスのイベントが残っていないか?これは開発用のみ
\r
48 'kill' : function(){
\r
49 var listeners, flag, p, i, list, timers, def;
\r
51 // TODO 破棄済のインスタンスへの kill
\r
53 if( this[ 'instanceOf' ]( X_EventDispatcher ) ){
\r
55 listeners = this[ '_listeners' ];
\r
57 // SEAL のタイミングは、イベント中なので listeners が存在する
\r
58 if( listeners && X_Class_SEAL_KILLING.length && X_Class_SEAL_KILLING.indexOf( this ) !== -1 ) return;
\r
60 // listeners がない場合、イベントの登録がないため、BEFORE_KILL_INSTANCE は呼ばれない。
\r
61 // KILL_RESERVED == true の場合、BEFORE_KILL_INSTANCE は呼ばれない。
\r
62 if( listeners && !listeners[ X_LISTENERS_KILL_RESERVED ] && listeners[ X_EVENT_BEFORE_KILL_INSTANCE ] ){
\r
63 X_Class_SEAL_KILLING[ i = X_Class_SEAL_KILLING.length ] = this;
\r
65 if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_CALLBACK_PREVENT_DEFAULT ){
\r
66 this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );
\r
67 // BEFORE_KILL_INSTANCE, KILL_INSTANCE_CANCELED 内で kill() しても PREVENT_DEFAULT の場合はこれを無視する。
\r
71 X_Class_SEAL_KILLING.length === 1 ?
\r
72 ( X_Class_SEAL_KILLING.length = 0 ) :
\r
73 X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING[ i ] === this ? i : X_Class_SEAL_KILLING.indexOf( this ), 1 );
\r
78 if( listeners = this[ '_listeners' ] ){// unlisten 等で listeners が破棄されている場合があるので取り直し。
\r
79 if( listeners[ X_LISTENERS_DISPATCHING ] ){
\r
80 listeners[ X_LISTENERS_KILL_RESERVED ] = true;
\r
84 if( listeners[ X_EVENT_KILL_INSTANCE ] ){
\r
85 X_Class_SEAL_KILLING[ i = X_Class_SEAL_KILLING.length ] = this;
\r
87 listeners[ X_LISTENERS_KILL_RESERVED ] = false;
\r
88 this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE );
\r
90 X_Class_SEAL_KILLING.length === 1 ?
\r
91 ( X_Class_SEAL_KILLING.length = 0 ) :
\r
92 X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING[ i ] === this ? i : X_Class_SEAL_KILLING.indexOf( this ), 1 );
\r
95 if( !( listeners = this[ '_listeners' ] ) ){
\r
96 for( p in listeners ){
\r
97 //if( X_EMPTY_OBJECT[ opt_type ] ) continue;
\r
98 if( p <= X_LISTENERS_KILL_RESERVED ) continue;
\r
99 list = listeners[ p ];
\r
100 for( i = list.length; i; ){
\r
101 this[ 'unlisten' ]( p, list[ --i ] );
\r
107 if( this[ 'instanceOf' ]( Node ) ){
\r
108 // console.log( 'KILL : ' + this.call( 'outerHTML' ) );
\r
109 X_Node_onKill( this );
\r
112 timers = X_EventDispatcher_LAZY_TIMERS;
\r
114 // asyncDispatch の削除
\r
115 for( p in timers ){
\r
116 if( timers[ p ] === this ){
\r
117 // delete X_EventDispatcher_LAZY_TIMERS[ p ]; コレ不要
\r
118 X_Timer_remove( p );
\r
123 X_Object_clear( this );
\r
125 def = X_Class_getClassDef( this );
\r
128 def.live.splice( def.live.indexOf( this ), 1 );
\r
129 def.pool[ def.pool.length ] = this;
\r
134 * 関数は Constructor 内で使用します。クラス定義を辿ってスーパークラスのコンストラクタを呼び出します。<br>
\r
135 * 内部的には、呼び出したコンストラクタは配列に控え(X_Class_CALLING_SUPER)、呼び出したコンストラクタ内でさらに Super が呼ばれた場合、配列を元にさらにスーパーなコンストラクタを辿ります。
\r
136 * @example Constructor : function( arg1, arg2 ){
\r
137 * this.Super( aeg1, arg2 );
\r
139 * @param var_args {...?} 親コンストラクタを呼ぶ際に渡す任意の数の引数
\r
142 // TODO 現在 new しているインスタンスを保持してチェックする
\r
143 'Super' : function( var_args ){
\r
145 sClass = me.constructor,
\r
146 i = X_Class_SUPER_CALLER.indexOf( me ),
\r
147 stack, t, def, ret;
\r
150 X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me;
\r
151 t = stack = X_Class_SUPER_STACKS[ i ] = 0;
\r
153 t = stack = X_Class_SUPER_STACKS[ i ];
\r
156 sClass = X_Class_getClassDef( sClass ).SuperClass;
\r
163 sClass = X_Class_getClassDef( sClass ).SuperClass;
\r
164 if( !sClass ) break;
\r
165 def = X_Class_getClassDef( sClass );
\r
167 if( def.Constructor ){
\r
168 X_Class_SUPER_STACKS[ i ] += t;
\r
169 ret = def.Constructor.apply( me, arguments );
\r
174 // index が替わっている可能性があるので取り直し
\r
175 if( X_Class_SUPER_CALLER[ i ] !== me ) i = X_Class_SUPER_CALLER.indexOf( me );
\r
177 if( X_Class_SUPER_STACKS[ i ] === stack ){
\r
178 //console.log( 'スーパークラス、またはスーパークラスのコンストラクタは存在しません' );
\r
182 X_Class_SUPER_CALLER.splice( i, 1 );
\r
183 X_Class_SUPER_STACKS.splice( i, 1 );
\r
185 X_Class_SUPER_STACKS[ i ] = stack;
\r
191 * myFunc について、スーパークラスで設定されている同名の関数を呼び出す。<br>
\r
192 * 低速な関数なので多用されるべきではありません!<br>
\r
193 * 第一引数に自身の(自身から参照できる)関数を指定します。内部では関数名を調べた上で prototype チェーンをゴリゴリ辿る、特別なことはしていません。<br>
\r
194 * superCall と Super がネストする場合も現在のクラス階層を X_Class_SUPER_CALLER, X_Class_SUPER_STACKS を使って控えているので、意図した親関数が呼ばれます。<br>
\r
195 * 次の理由によって、関数名で辿ることは非推奨です。
\r
197 * <li>closur compiler でメソッド名が変更される
\r
199 * 次の場合、意図した動作が得られません。
\r
201 * <li>2つ以上の異なる名前で同じ関数がメンバーがいた場合
\r
202 * <li>サブクラスの prototype にスーパークラスと同じ関数をコピーしている
\r
203 * <li>非関数でメンバーを上書きしている
\r
204 * <li>superCall 以外の手段で親関数を呼び、そのなかで superCall を呼んだ
\r
206 * 通常の X.Class.create の書き方ではこのような状況は起きませんが、js はなんでもいろいろ出来てしまいますから…<br>
\r
207 * 参考:<a href="http://qiita.com/no22@github/items/d3bead2acbb7ff1fb86b" target="_blank">ES5なJavascriptでモダンなクラス的継承&スーパー呼び出し </a><br>
\r
208 * original:<a href="http://javascript.crockford.com/inheritance.html" target="_blank">Classical Inheritance in JavaScript</a>
\r
209 * @param myFunc {Function|string} オーバーライド済の自身の(自身から参照できる)関数。
\r
210 * @param var_args {...*} オーバーライド元関数に渡す任意の数の引数
\r
211 * @example return this.superCall( this.myFunc, param0, param1, ... );
\r
212 * @return {*} オーバーライド元の関数を呼び出した戻り値。
\r
214 'superCall' : function( myFunc, var_args ){
\r
216 sClass = me.constructor,
\r
217 proto = sClass.prototype,
\r
218 i = X_Class_SUPER_CALLER.indexOf( me ),
\r
220 p, name, stack, t, sFunc, ret;
\r
222 if( X_Type_isFunction( myFunc ) ){
\r
224 if( proto[ p ] === myFunc ){
\r
229 if( !name ) return;
\r
231 if( X_Type_isString( myFunc ) && X_Type_isFunction( me[ myFunc ] ) ){
\r
238 X_Class_SUPER_CALLER[ i = X_Class_SUPER_CALLER.length ] = me;
\r
239 t = stack = X_Class_SUPER_STACKS[ i ] = 0;
\r
241 t = stack = X_Class_SUPER_STACKS[ i ];
\r
244 sClass = X_Class_getClassDef( sClass ).SuperClass;
\r
250 myFunc = sClass.prototype[ name ];
\r
254 sClass = X_Class_getClassDef( sClass ).SuperClass;
\r
255 sFunc = sClass.prototype[ name ];
\r
257 if( sFunc !== myFunc /* X_Object_own( name, sClass.prototype ) */ ){
\r
258 if( X_Type_isFunction( sFunc ) ){
\r
259 X_Class_SUPER_STACKS[ i ] += t;
\r
260 switch( args.length ){
\r
262 ret = sFunc.call( me );
\r
265 ret = sFunc.call( me, args[ 1 ] );
\r
268 ret = sFunc.call( me, args[ 1 ], args[ 2 ] );
\r
271 ret = sFunc.call( me, args[ 1 ], args[ 2 ], args[ 3 ] );
\r
274 args = X_Array_copy( args );
\r
276 ret = sFunc.apply( me, args );
\r
285 // index が替わっている可能性があるので取り直し
\r
286 if( X_Class_SUPER_CALLER[ i ] !== me ) i = X_Class_SUPER_CALLER.indexOf( me );
\r
289 X_Class_SUPER_CALLER.splice( i, 1 );
\r
290 X_Class_SUPER_STACKS.splice( i, 1 );
\r
292 X_Class_SUPER_STACKS[ i ] = stack;
\r
298 * インスタンスのクラスか?またはスーパークラスか?調べる。<br>
\r
299 * instanceof 構文をサポートしない環境(IE5以下)を想定する場合、必ずこのメソッドを使用すること。<br>
\r
300 * クラスのインスタンスか?だけ調べたい場合は this.constructor === klass が高速。
\r
301 * @param klass {__ClassBase__} クラス定義
\r
302 * @return {boolean}
\r
304 // TODO instanceof に対応したブラウザはそちらを使用
\r
305 'instanceOf' : function( klass ){
\r
308 if( this.constructor === klass ) return true;
\r
309 while( Super = X_Class_getClassDef( Super ).SuperClass ){
\r
310 if( Super === klass ) return true;
\r
316 // ------------------------------------------------------------------------- //
\r
317 // --- interface ----------------------------------------------------------- //
\r
318 // ------------------------------------------------------------------------- //
\r
333 * <p>Class を定義し システムの管理下に置く。
\r
334 * <p>prototype 継承のブラウザ毎の差異も吸収し、 以下から最適な方法をしてくれる。
\r
337 * <li>Object.create はパフォーマンスが悪そうなので現在は使っていない。
\r
338 * <li>SubClass.prototype.__proto__ = SuperClass.prototype;
\r
339 * <li>SubClass.prototype = new SuperClass;
\r
343 * <li>X.Class.create( opt_settings, opt_name, opt_props ) でクラスを登録.
\r
344 * <li>コンストラクタ となるメソッドは、opt_props 内の Constructor : function( arg ){ ... }, に書く.
\r
345 * <li>通常通り new で インスタンス生成
\r
346 * <li>kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.
\r
347 * <li>pool が有効の場合、new で pool されたインスタンスが返される.
\r
349 * @namespace X.Class
\r
352 X[ 'Class' ] = /** @lends X.Class */ {
\r
358 'NONE' : X_Class.NONE,
\r
360 // TODO この指定、フレームワーク内だけ!
\r
362 * インスタンスは破棄時(this.kill())に回収され、次回の new MyClass() 時に再利用されます。
\r
365 'POOL_OBJECT' : X_Class.POOL_OBJECT,
\r
368 * 定義するクラスは抽象クラスになります。new AbstractClass() とするとエラーになります。
\r
371 'ABSTRACT' : X_Class.ABSTRACT,
\r
377 'FINAL' : X_Class.FINAL,
\r
380 * 未実装。でも目印になるので付けておきましょう。
\r
383 'SINGLETON' : X_Class.SINGLETON,
\r
385 'create' : X_Class_create
\r
392 // ------------------------------------------------------------------------- //
\r
393 // --- implements ---------------------------------------------------------- //
\r
394 // ------------------------------------------------------------------------- //
\r
397 * X.Class.create() によるクラス定義は必ずしもコンストラクタ('Constructor')を必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。
\r
398 * @alias X.Class.create
\r
399 * @param {string} [displayName] クラスの名前
\r
400 * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など
\r
401 * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと
\r
402 * @return {__ClassBase__}
\r
403 * @example var myClass = X.Class.create(
\r
408 * Constructor : function( obj ){
\r
409 * this.name = obj.name;
\r
411 * getName : function(){
\r
412 * return this.name;
\r
414 * setName : function(v){
\r
420 function X_Class_create( /* displayName, classSetting, privateClass, props */ ){
\r
421 var args = X_Array_copy( arguments ),
\r
422 displayName = args[ 0 ],
\r
424 opt_pool, opt_abstract, opt_final,
\r
429 cbHash = { proxy : X_Class_actualConstructor, classDef : classDef };
\r
431 if( X_Type_isString( displayName ) === true ){
\r
432 classDef.displayName = displayName;
\r
437 classDef.setting = classSetting = args[ 0 ];
\r
438 if( X_Type_isNumber( classSetting ) ){
\r
439 opt_pool = !!( classSetting & X_Class.POOL_OBJECT );
\r
440 opt_abstract = !!( classSetting & X_Class.ABSTRACT );
\r
441 opt_final = !!( classSetting & X_Class.FINAL );
\r
442 if( opt_final && opt_abstract ){
\r
443 X.Logger.critical( 'final & Abstract!' );
\r
448 classDef.setting = 0;
\r
453 if( !X_Type_isObject( props ) ){
\r
454 // クラスメンバ用オブジェクトが無しでもクラスは作成可能
\r
457 if( props[ 'Constructor' ] ){
\r
459 if( !X_Type_isFunction( props[ 'Constructor' ] ) ){
\r
460 alert( '"Constructor" is not function.' );
\r
464 classDef.Constructor = props[ 'Constructor' ];
\r
467 klass = X_Closure_actualClosure( cbHash ); // TODO callbackHash を class定義の置き場所にしてしまう!なるほど…
\r
468 cbHash.klass = klass;
\r
469 klass[ 'superClassOf' ] = X_Class_superClassOf;
\r
470 klass[ 'subClassOf' ] = X_Class_subClassOf;
\r
472 if( X_Class_useObjectCreate ){
\r
473 klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonMethods, false );
\r
474 klass.prototype.constructor = klass;
\r
476 if( X_Class_use_proto_ ){
\r
477 X_Class_override( klass.prototype, props, true );
\r
478 if( X_Class_traits ){
\r
479 klass.prototype.__proto__ = X_Class_traits;
\r
481 X_Class_override( klass.prototype, X_Class_CommonMethods, false );
\r
484 klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonMethods, false );
\r
485 klass.prototype.constructor = klass;
\r
488 klass[ 'NAME' ] = displayName;
\r
490 if( opt_abstract ){
\r
491 classDef.isAbstract = true;
\r
494 classDef.pool = [];
\r
495 classDef.live = [];
\r
498 classDef.Final = true;
\r
500 klass[ 'inherits' ] = X_Class_inherits;
\r
503 X_Class_CLASS_LIST.push( klass );
\r
504 X_Class_DEF_LIST.push( classDef );
\r
511 function X_Class_getClass( instance ){
\r
512 var cList = X_Class_CLASS_LIST, i;
\r
514 if( ( i = cList.indexOf( instance.constructor ) ) !== -1 ) return cList[ i ];
\r
515 if( cList.indexOf( instance ) !== -1 ) return instance;
\r
518 // TODO def = klass( X_Closure_COMMAND_BACK )
\r
519 function X_Class_getClassDef( KlassOrInstance ){
\r
520 var i = X_Class_CLASS_LIST.indexOf( KlassOrInstance );
\r
521 if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );
\r
522 if( i !== -1 ) return X_Class_DEF_LIST[ i ];
\r
524 if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;
\r
527 /* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */
\r
528 function X_Class_override( target, src, force ){
\r
531 if( p === 'Constructor' ) continue;
\r
532 if( p === '__proto__' || p === 'prototype' || p === 'constructor' ){
\r
533 X.Logger.critical( p + ' is reserved!' );
\r
536 if( force || target[ p ] === undefined ){
\r
537 target[ p ] = src[ p ];
\r
545 * @alias __ClassBase__.superClassOf
\r
546 * @param klass {__ClassBase__}
\r
547 * @return {boolean}
\r
549 function X_Class_superClassOf( klass ){
\r
550 var myDef = X_Class_getClassDef( this ),
\r
551 targetDef = X_Class_getClassDef( klass ),
\r
552 SuperClass = klass;
\r
554 if( !myDef || !targetDef || this === klass ) return false;
\r
556 while( SuperClass = X_Class_getClassDef( SuperClass ).SuperClass ){
\r
557 if( SuperClass === this ) return true;
\r
565 * @alias __ClassBase__.subClassOf
\r
567 * @param klass {__ClassBase__}
\r
568 * @return {boolean}
\r
570 function X_Class_subClassOf( klass ){
\r
571 return klass && X_Class_superClassOf.call( klass, this );
\r
575 * サブクラスを作ります。与える引数は X_Class.create と同じです。http://d.hatena.ne.jp/m-hiyama/20051018/1129605002
\r
576 * @alias __ClassBase__.inherits
\r
577 * @example var SubClass = SuperClass.inherits( 'Sub', X_Class.FINAL, { ... } );
\r
578 * @param {string} [displayName] クラスの名前
\r
579 * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など
\r
580 * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと
\r
581 * @return {__ClassBase__}
\r
583 function X_Class_inherits( /* displayName, classSetting, props */ ){
\r
584 var args = X_Array_copy( arguments ),
\r
587 superDef = X_Class_getClassDef( Super ),
\r
588 displayName = args[ 0 ],
\r
592 if( superDef.Final ) X.Logger.critical( 'X.Class inherits, Class is final!' );
\r
595 if( X_Type_isString( displayName ) ){
\r
598 displayName = 'SubClass of ' + superDef.displayName;
\r
600 params.push( displayName );
\r
603 classSetting = args[ 0 ];
\r
604 if( X_Type_isNumber( classSetting ) ){
\r
607 // クラス設定がない場合、親からコピーして、Abstract flag は落とす??
\r
608 classSetting = superDef.setting;// &= ~X_Class.ABSTRACT;
\r
611 params.push( classSetting );
\r
614 if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){
\r
615 params.push( args.shift() );
\r
619 params.push( args[ 0 ] );
\r
622 if( X_Class_useObjectCreate ){
\r
623 X_Class_traits = Object.create( Super.prototype );
\r
625 if( X_Class_use_proto_ ){
\r
626 X_Class_traits = Super.prototype;
\r
628 X_Class_traits = new Super( X_Closure_COMMAND_DROP );
\r
630 klass = X_Class_create.apply( X.Class, params );
\r
631 X_Class_traits = null;
\r
633 def = X_Class_getClassDef( klass );
\r
635 def.SuperClass = Super;
\r
636 //def.SuperProto = Super.prototype;
\r
637 //def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor;
\r
643 * new の実体.コンストラクタの機能は instance.Constructor に書く.
\r
644 * これにより pool された オブジェクト(破棄されたインスタンス) を再利用できる
\r
646 function X_Class_actualConstructor( f, args ){
\r
647 var klass = f.klass,
\r
651 if( def.isAbstract ){
\r
652 X.Logger.critical( 'AbstractClass!' );
\r
656 instance = def.pool && def.pool.length ?
\r
658 X_Class_useObjectCreate ?
\r
659 Object.create( klass.prototype ) :
\r
660 new klass( X_Closure_COMMAND_DROP );
\r
662 def.live && def.live.push( instance );
\r
664 if( X_Class_constructorFix && instance.constructor !== klass ){
\r
665 console.log( '------- constructor の不一致!' ); // Android2.3.7
\r
666 instance.constructor = klass;
\r
669 obj = def.Constructor ?
\r
670 def.Constructor.apply( instance, args ) :
\r
671 def.SuperClass && instance[ 'Super' ].apply( instance, args );
\r
673 if( obj !== instance && ( X_Type_isObject( obj ) || X_Type_isFunction( obj ) ) ){ // Class
\r
674 instance[ 'kill' ]();
\r
681 console.log( 'X.Core.Class' );
\r