X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F01_core%2F13_XEventDispatcher.js;h=25c2d11477034a160a6fcb231065e418636740b8;hb=9a3ec59dd9eafb9fde293ee2f112e168af1e62bc;hp=7ff83b395829e665c588d390456f0cd0d726b519;hpb=dd02887497fa95f13d112b7fc2e5e7aefd0ffb08;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/01_core/13_XEventDispatcher.js b/0.6.x/js/01_core/13_XEventDispatcher.js index 7ff83b3..25c2d11 100644 --- a/0.6.x/js/01_core/13_XEventDispatcher.js +++ b/0.6.x/js/01_core/13_XEventDispatcher.js @@ -1,4 +1,8 @@ /** + * + */ + +/** * X.EventDispatcher * * 1. as3 の EventDispatcher ライクなクラス。そのまま使ったり、継承したり。コールバック中にイベントを追加したら?削除したら?にも対処している。 @@ -32,30 +36,32 @@ var X_EventDispatcher_once = false, // ------------------------------------------------------------------------- // /** - * イベントターゲット(widnow, document, Image, XHR, Silverlight 等)をラップする場合、通常は new 時に渡します。参照:コンストラクタ実体 {@link X.EventDispatcher.Constructor} - * アプリケーション独自のイベントをやり取りしたいだけ、という場合、イベントターゲットは不要です。 + *

イベントターゲット(widnow, document, Image, XHR, Silverlight 等)をラップする場合、通常は new 時に渡します。 + *

参照:コンストラクタ実体 {@link X.EventDispatcher.Constructor} + *

アプリケーション独自のイベントをやり取りしたい、という場合、イベントターゲットは不要です。 + * * @class * @classdesc EventTarget オブジェクトをラップしたり、アプリケーションで独自に定義したイベントを発信するためのクラスです。 - * listen, unlisten, dispatch という addEventListener, removeEventListener, dispatchEvent に対応する関数を持ちます。 - * さらに listening という as3 の hasEventListener に相当する関数を持ちます。 - * イベントターゲットオブジェクト(widnow, document, HTMLElement, XHR 等)が _rawObject に設定されていた場合に、それらへ実際のイベント登録・解除も行います。 - * このイベントの登録・解除はクロスブラウザで、IE5~8 の独自イベントの差異を吸収し、DOM0 に対しても複数のコールバックを登録することができます。 - * またコールバックに対して、this コンテキストや、追加の引数を指定もできます。 this コンテキストを指定しなかった場合、EventDispatcher インスタンスがコールバックの this になります。 - * unlisten() は、引数を指定しなかった場合、全てのイベントを解除します。ただし、systemListen 経由で登録されたハンドラは解除されません。 + *

listen, unlisten, dispatch という addEventListener, removeEventListener, dispatchEvent に対応する関数を持ちます。 + *

また listening という ActionScript3 の hasEventListener に相当する関数を持ちます。 + *

イベントターゲットオブジェクト(widnow, document, HTMLElement, XHR 等)が this._rawObject に設定されていた場合に、それらへ実際のイベント登録・解除も行います。 + *

このイベントの登録・解除はクロスブラウザで、IE5~8 の独自イベントの差異を吸収し、DOM0 に対しても複数のコールバックを登録することができます。 + *

またコールバックに対して、this コンテキストや、追加の引数を指定もできます。 this コンテキストを指定しなかった場合、EventDispatcher インスタンスがコールバックの this になります。 + *

unlisten() は、引数を指定しなかった場合、全てのイベントを解除します。ただし、systemListen 経由で登録されたハンドラは解除されません。 * systemListen, systemUnlisten は、ライブラリ内のコードからしかアクセスできません。 - * @param {object=} opt_rawObject + * + * @augments X_Class_CommonProps + * + * @param {object=} opt_rawObject イベントターゲット(EventTarget) */ X.EventDispatcher = X.Class.create( 'EventDispatcher', - /** @lends {X.EventDispatcher.prototype} */ + /** @lends X.EventDispatcher.prototype */ { - /** - * @namespace - * @memberof X.EventDispatcher - */ + // TODO _rawObjectType EventTarget, XHR, Silverlight, ... /** @@ -68,9 +74,9 @@ X.EventDispatcher = /** * _rawObject には HTMLElement, window, document, XHR といったイベントターゲットオブジェクトを設定します。 - * _rawObject が設定されていると on(), off() 時に addEventListener(DOM Level2) や detachEvent(ie5~8), on~(DOM0) 等を操作します。 - * _rawObject は最初の on() 前に設定しておかないと addEventListener 等が意図したように行われません。 - * X.Node では非同期に HTMLElement を生成していて、要素生成以前に on, off を呼び出すことができます。これは適宜に migrateEvent, restoreEvent を呼んで解決しているためです。 + * _rawObject が設定されていると listen(), unlisten() 時に addEventListener(DOM Level2) や detachEvent(ie5~8), on~(DOM0) 等を操作します。 + * _rawObject は最初の listen() 前に設定しておかないと addEventListener 等が意図したように行われません。 + * X.Node では非同期に HTMLElement を生成していて、要素生成以前に listen, unlisten を呼び出すことができます。これは適宜に X_EventDispatcher_toggleAllEvents を呼んで解決しているためです。 * @private * @type {Object} */ @@ -84,7 +90,7 @@ X.EventDispatcher = */ '_handleEvent' : null, - /* + /** * dispatch 中に dispatch が呼ばれた際に、そのネストの深さを保存する。 * dispatch() 終了時に _dispatching が 0 の場合に、現在のインスタンスの dispatch がすべて終わったことになる。 * @private @@ -92,7 +98,7 @@ X.EventDispatcher = */ '_dispatching' : 0, // dispatch 中の unlisten で使用 - /* + /** * dispatch 中に listen が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。 * _dispatching が 0 のときに _reserves を使って listen() を呼び出す。 * @private @@ -100,7 +106,7 @@ X.EventDispatcher = */ '_reserves' : null, - /* + /** * dispatch 中に unlisten が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。 * _dispatching が 0 のときに _unlistens を使って unlisten() を呼び出す。 * @private @@ -108,18 +114,26 @@ X.EventDispatcher = */ '_unlistens' : null, - /* + /** * dispatch 中に kill が呼ばれた場合に、X.EventDispatcher インスタンスの削除を dispatch 後にずらすために立てるフラグ。 * @private * @type {boolean} */ '_killReserved' : false, + + /** + * asyncDispatch 中に kill が呼ばれた場合に、X.EventDispatcher インスタンスの削除を最後のタイマーの発火後にずらすために立てるフラグ。 + * TODO asyncDispatch の解除はどうする? + * @private + * @type {boolean} + */ + '_lastLazyID' : false, /** * X.EventDispatcher のコンストラクタの実体。 - * @constructs + * @@@constructs * @this {X.EventDispatcher} - * @params {object=} opt_rawObject + * @param {object=} opt_rawObject */ Constructor : function( opt_rawObject ){ if( opt_rawObject ){ @@ -134,12 +148,16 @@ X.EventDispatcher = * @param {(eventHash|string|number)} e */ dispatch : X_EventDispatcher_dispatch, - - /** - * - * @this {X.EventDispatcher} - */ - on : X_EventDispatcher_listen, + + /** + * + * @this {X.EventDispatcher} + * @param {(string|number|Array.<(string,number)>)} type + * @param {(listener|function|Array)=} opt_arg1 + * @param {(function|Array=} opt_arg2 + * @param {Array=} opt_arg3 + * @return {X.EventDispatcher} + */ listen : X_EventDispatcher_listen, /** @@ -157,17 +175,19 @@ X.EventDispatcher = X_EventDispatcher_once = false; return this; }, - - off : X_EventDispatcher_unlisten, + unlisten : X_EventDispatcher_unlisten, /** - * イベントリスナの登録状況を真偽値で返す。戻り値が数値(index)の場合もあるが、これは内部のみで使用。 - * listening() と type を省略した場合、一つでも登録があれば true を返す。 - * listening( type ) と type だけを与えた場合、その type に登録があれば true を返す。 - * type と イベントリスナの組み合わせが登録されているかを調べる場合は、listen 時の thisObject や args(Array) も一致させて渡す必要がある。 + *

イベントリスナの登録状況を真偽値で返す。戻り値が数値(index)の場合もあるが、これは内部のみで使用。 + *

this.listening(); のように type を省略した場合、一つでも登録があれば true を返す。 + *

this.listening( 'myevent' ); と type だけを与えた場合、その type に登録があれば true を返す。 + *

type と イベントリスナの組み合わせが登録されているかを調べる場合は、listen 時の thisObject や args(Array) も一致させて渡す必要がある。 + * + * @example * this.listen( [ 'myevent', 'yourevent' ], this, onMyEvent, args = [ 1, 'a' ] ); * this.listening( 'myevent', this, onMyEvent, args ) === true; + * * @this {X.EventDispatcher} * @return {(number|boolean)} * @param {(string|number)=} opt_type @@ -217,7 +237,7 @@ X.EventDispatcher = e = delay; delay = 0; }; - return X.Timer.add( delay, 1, this, this.dispatch, [ e ] ); + return this[ '_lastLazyID' ] = X.Timer.add( delay, 1, this, X_EventDispatcher_dispatch, [ e ] ); } } ); @@ -285,6 +305,11 @@ function X_EventDispatcher_dispatch( e ){ }; if( ( --this._dispatching ) === 0 ){ + + if( this[ '_lastLazyID' ] && X_Timer_currentUID === this[ '_lastLazyID' ] ){ + delete this[ '_lastLazyID' ]; + }; + // dispatch 中に unlisten された要素の削除 unlistens = this._unlistens; delete this._dispatching; @@ -292,6 +317,7 @@ function X_EventDispatcher_dispatch( e ){ // _unlistens に入っている callbackHash は、lock をクリアしている X_EventDispatcher_unlock = true; for( type in unlistens ){ + //if( X_EMPTY_OBJECT[ type ] ) continue; list = unlistens[ type ]; for( i = list.length; i; ){ this.unlisten( type, list[ --i ] ); @@ -325,11 +351,12 @@ function X_EventDispatcher_dispatch( e ){ /** * * @this {X.EventDispatcher} - * @return {X.EventDispatcher} + * @memberOf X.EventDispatcher.prototype * @param {(string|number|Array.<(string,number)>)} type * @param {(listener|function|Array)=} opt_arg1 * @param {(function|Array=} opt_arg2 * @param {Array=} opt_arg3 + * @return {X.EventDispatcher} */ function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){ var list = this._listeners, @@ -406,6 +433,7 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){ if( opt_type === undefined ){ // 全て削除 for( opt_type in list ){ + //if( X_EMPTY_OBJECT[ opt_type ] ) continue; _list = list[ opt_type ]; for( i = _list.length; i; ){ this.unlisten( opt_type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用 @@ -476,7 +504,7 @@ function X_EventDispatcher_addEvent( that, type, raw, list ){ if( X.Type.isArray( type ) ){ for( i = type.length; i; ){ X_EventDispatcher_systemListen( that, type[ --i ], X.emptyFunction ); - console.log( 'X_EventDispatcher_systemListen ' + type[ i ] ); + console.log( 'events fix > ' + type[ i ] ); }; } else { X_EventDispatcher_actualAddEvent( that, type, raw, list ); @@ -496,7 +524,17 @@ var X_EventDispatcher_actualAddEvent = if( that._isSilverlight ){ list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); list.sltoken = raw.AddEventListener( type, list.slcallback ); - } else { + } else + // iOS と MacOSX Iron36 で発生。連続してアニメーションが起こると、クロージャの束縛された obj へのアクセスに失敗する。Win では起きない? + // むしろ、MacOSX のブラウザ全般で起こる?? + if( ( X_UA.WebKit || X_UA.Blink ) && + ( type === 'webkitTransitionEnd' || type === 'transitionend' || + type === 'animationend' || type === 'webkitAnimationEnd' || + type === 'animationstart' || type === 'webkitAnimationStart' || + type === 'animationiteration' || type === 'webkitAnimationIteration' ) ){ + raw.addEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false ); + } else { + console.log( 'event > ' + type ); that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) ); if( raw.addEventListener ){ @@ -541,6 +579,16 @@ var X_EventDispatcher_actualAddEvent = }); /* + * iOS の webkitTransitionEnd が連続して起こる場合、 + * コールバックの(that._handleEvent)クロージャ内の実際のコールバック(X_Callback_actualClosure:obj._)が + * 参照できていない問題に遭遇、、、iOS3.1.3 & iOS6.1.5 で確認 + * animation も怪しい、、、 + */ +function X_EventDispatcher_iOSTransitionEndDispatch( e ){ + return X_Node_getXNode( this ).dispatch( X_Event_RenameTo[ e.type ] || e.type ); +}; + +/* * Silverlight のイベントの概要 * http://msdn.microsoft.com/ja-jp/library/cc189018%28v=vs.95%29.aspx#the_sender_parameter_and_event_data */ @@ -573,7 +621,15 @@ var X_EventDispatcher_actualRemoveEvent = X_Callback_correct( list.slcallback ); delete list.sltoken; delete list.slcallback; + } else + if( ( X_UA.WebKit || X_UA.Blink ) && + ( type === 'webkitTransitionEnd' || type === 'transitionend' || + type === 'animationend' || type === 'webkitAnimationEnd' || + type === 'animationstart' || type === 'webkitAnimationStart' || + type === 'animationiteration' || type === 'webkitAnimationIteration' ) ){ + raw.removeEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false ); } else { + if( raw.addEventListener ){ raw.removeEventListener( type, that._handleEvent, false ); } else { @@ -661,7 +717,7 @@ var X_EventDispatcher_actualHandleEvent = var ev = new X.Dom.Event( e, this ), ret = X_Callback_NONE, i, l; - console.log( '>>>>>>>>>> ' + e.type ); + //console.log( '>>>>>>>>>> ' + e.type ); // touch event -> pointer if( X.Type.isArray( ev ) ){ if( ev.length === 0 ){ @@ -712,6 +768,7 @@ function X_EventDispatcher_toggleAllEvents( that, add ){ type; if( !list || !raw ) return; for( type in list ){ + //if( X_EMPTY_OBJECT[ type ] ) continue; // 数字イベントの除外 if( '' + parseFloat( type ) !== type ){ // TODO type rename はここ