X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F01_core%2F13_XEventDispatcher.js;h=25c2d11477034a160a6fcb231065e418636740b8;hb=dad4215398716c9913e80ec902c76db6762c1dce;hp=426a05dd762ab1ab8e368357f6e2dc8d1309aab7;hpb=541618cd9485cb041f46177d6869cc6d618ed1da;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 426a05d..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 ライクなクラス。そのまま使ったり、継承したり。コールバック中にイベントを追加したら?削除したら?にも対処している。 @@ -21,68 +25,45 @@ // ------------ local variables -------------------------------------------- // // ------------------------------------------------------------------------- // var X_EventDispatcher_once = false, + X_EventDispatcher_lock = false, + X_EventDispatcher_unlock = false, X_EventDispatcher_needsIndex = false, - X_EventDispatcher_temp = {}, - - X_EventDispatcher_safariPreventDefault = false; // // Safari3- - -if( X.UA.MacIE ){ - X_EventDispatcher_temp.DOM_W3C = true; - X_EventDispatcher_temp.EVENT_DOM0 = true; -} else -if( X.UA.IE4 ){ // ie4 & iemobi4 - X_EventDispatcher_temp.DOM_IE4 = true; - X_EventDispatcher_temp.EVENT_DOM0 = true; -} else -if( document.getElementById ){ - X_EventDispatcher_temp.DOM_W3C = true; - if( document.addEventListener ){ - X_EventDispatcher_temp.EVENT_W3C = true; - } else - if( document.attachEvent ){ - X_EventDispatcher_temp.EVENT_IE = true; - } else { - X_EventDispatcher_temp.EVENT_DOM0 = true; - }; -} else -if( document.all ){ - X_EventDispatcher_temp.DOM_IE4 = true; - X_EventDispatcher_temp.EVENT_DOM0 = true; -} else -if( document.layers ){ -} else { - -}; + X_EventDispatcher_safariPreventDefault = false; // Safari3- // ------------------------------------------------------------------------- // // --- interface ----------------------------------------------------------- // // ------------------------------------------------------------------------- // /** - * イベントターゲット(widnow, document, Image, XHR 等)をラップする場合、通常は 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 になります。 - * @param {object=} opt_rawObject + *

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 は、ライブラリ内のコードからしかアクセスできません。 + * + * @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, ... /** * イベントリスナをイベント名(string)や数値(1~,フレームワーク内で定義)をキーとするArrayで記憶します。 * Arrayには、{k:種類,x:コンテキスト(thisObject),f:関数,s:サプリメントする引数の配列} というハッシュ、または関数が蓄えられています。 @@ -93,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.Dom.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} */ @@ -109,7 +90,7 @@ X.EventDispatcher = */ '_handleEvent' : null, - /* + /** * dispatch 中に dispatch が呼ばれた際に、そのネストの深さを保存する。 * dispatch() 終了時に _dispatching が 0 の場合に、現在のインスタンスの dispatch がすべて終わったことになる。 * @private @@ -117,7 +98,7 @@ X.EventDispatcher = */ '_dispatching' : 0, // dispatch 中の unlisten で使用 - /* + /** * dispatch 中に listen が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。 * _dispatching が 0 のときに _reserves を使って listen() を呼び出す。 * @private @@ -125,7 +106,7 @@ X.EventDispatcher = */ '_reserves' : null, - /* + /** * dispatch 中に unlisten が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。 * _dispatching が 0 のときに _unlistens を使って unlisten() を呼び出す。 * @private @@ -133,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 ){ @@ -159,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, /** @@ -182,21 +175,23 @@ 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 - * @param {(listener|function|Array)=} opt_arg1 + * @param {(listener|function|Array|callbackHash)=} opt_arg1 * @param {(function|Array=} opt_arg2 * @param {Array=} opt_arg3 */ @@ -211,7 +206,7 @@ X.EventDispatcher = if( opt_arg1.k ){ hash = opt_arg1; } else { - hash = X.Callback._classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this ); + hash = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this ); }; if( ( unlistens = this._unlistens ) && ( unlistens = unlistens[ opt_type ] ) ){ @@ -222,7 +217,10 @@ X.EventDispatcher = }; for( i = list.length; i; ){ f = list[ --i ]; - if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ) return X_EventDispatcher_needsIndex ? i : true; + if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ){ + // index を要求された場合、lock されていない、または unlock なら index を返す + return X_EventDispatcher_needsIndex ? ( X_EventDispatcher_unlock || !f.lock ? i : false ) : true; + }; }; return false; }, @@ -230,12 +228,16 @@ X.EventDispatcher = /** * delay(ミリ秒)後にイベントを dispatch する。戻り値は uid = X.Timer.add() のタイマーID(数値)。X.Timer.remove(uid) でタイマーを解除して dispatch を中止できる。 * @this {X.EventDispatcher} - * @return {number} - * @param {number=} delay - * @param {(eventHash|string|number)=} e + * @param {(number|eventHash|string)=} delay ms 省略した場合は 0 として扱う asyncDispatch( 'myevent' ) -> asyncDispatch( 0, 'myevent' ) + * @param {(eventHash|string|number)=} e イベントを表す数値、文字列、{ type : XXX, ... } なオブジェクト + * @return {number} X.Timer.add() の戻り値 */ asyncDispatch : function( delay, e ){ - return X.Timer.add( delay, 1, this, this.dispatch, [ e ] ); + if( delay && e === undefined ){ + e = delay; + delay = 0; + }; + return this[ '_lastLazyID' ] = X.Timer.add( delay, 1, this, X_EventDispatcher_dispatch, [ e ] ); } } ); @@ -252,9 +254,9 @@ X.EventDispatcher = */ function X_EventDispatcher_dispatch( e ){ var list = this[ '_listeners' ], - ret = X.Callback.NONE, + ret = X_Callback_NONE, type = e[ 'type' ], - unlistens, i, l, f, r, sysOnly; + unlistens, i, l, args, f, r, sysOnly; if( !list || !( list = list[ type || e ] ) ) return ret; @@ -279,15 +281,15 @@ function X_EventDispatcher_dispatch( e ){ }; if( unlistens && unlistens.indexOf( f ) !== -1 ) continue; - r = X.Callback.NONE; - if( f.k ){ - f.a = [ e ]; - r = X.Callback._proxyCallback( f ); - } else { - r = f.call( this, e ); - }; + //if( f !== X.emptyFunction ){ + // if( f.k ){ + r = X_Callback_proxyCallback( f, args || ( args = [ e ] ) ); + // } else { + // r = f.call( this, e ); + // }; + //}; - if( f.once || r & X.Callback.UN_LISTEN ){ + if( f.once || r & X_Callback_UN_LISTEN ){ // dispatch 中に unlisten が作られることがある if( !unlistens ){ unlistens = this._unlistens || ( this._unlistens = {} ); @@ -303,12 +305,19 @@ 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; delete this._unlistens; - + // _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 ] ); @@ -316,6 +325,7 @@ function X_EventDispatcher_dispatch( e ){ list.length = 0; delete unlistens[ type ]; }; + X_EventDispatcher_unlock = false; if( this._killReserved ){ this.kill(); @@ -324,8 +334,10 @@ function X_EventDispatcher_dispatch( e ){ for( i = 0, l = list.length; i < l; ++i ){ f = list[ i ]; X_EventDispatcher_once = f[ 4 ]; + X_EventDispatcher_lock = f[ 5 ]; this.listen( f[ 0 ], f[ 1 ], f[ 2 ], f[ 3 ] ); X_EventDispatcher_once = false; + X_EventDispatcher_lock = false; f.length = 0; }; list.length = 0; @@ -339,19 +351,22 @@ 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, i, raw, add, f; + + if( !type ) return this; if( this._dispatching ){ if( !this._reserves ) this._reserves = []; - this._reserves[ this._reserves.length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once ]; + this._reserves[ this._reserves.length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ]; return this; }; @@ -362,23 +377,35 @@ function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){ return this; }; - raw = this._rawObject || this._ie4getRawNode && this._ie4getRawNode(); + raw = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); add = raw && ( !list || !list[ type ] ) && X.Type.isString( type ); - if( this.listening( type, opt_arg1, opt_arg2, opt_arg3 ) ) return this; + if( this.listening( type, opt_arg1 || this, opt_arg2, opt_arg3 ) ) return this; if( !list ) list = this._listeners = {}; if( !( list = list[ type ] ) ) list = this._listeners[ type ] = []; - add && X_EventDispatcher_actualAddEvent( this, type, raw, list ); + add && X_EventDispatcher_addEvent( this, type, raw, list ); - f = X.Callback._classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this ); + f = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this ); list[ list.length ] = f; f.once = X_EventDispatcher_once; + f.lock = X_EventDispatcher_lock; return this; }; +/* + * X_EventDispatcher_systemUnlisten 経由でないと解除できないリスナの登録 + */ +function X_EventDispatcher_systemListen( that, type, opt_arg1, opt_arg2, opt_arg3 ){ + X_EventDispatcher_lock = true; + that.listen( type, opt_arg1, opt_arg2, opt_arg3 ); + X_EventDispatcher_lock = false; +}; + +// TODO this.listen(type) は this リスナの登録なのに、this.unlisten(type)は全てのtypeの削除、と不一致 + /** * * @this {X.EventDispatcher} @@ -396,6 +423,9 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){ if( X.Type.isArray( opt_type ) ){ for( i = opt_type.length; i; ){ this.unlisten( opt_type[ --i ], opt_arg1, opt_arg2, opt_arg3 ); + if( !opt_type[ i ] ){ + alert( '不正な unlisten Array' ); + }; }; return this; }; @@ -403,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 を使用 @@ -437,108 +468,127 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){ if( i === false ) return this; f = ( _list = list[ opt_type ] )[ i ]; + // _unlistens に入っている callbackHash は、lock をクリアしている if( unlistens = this._unlistens ){ ( unlistens = unlistens[ opt_type ] ) ? ( unlistens[ unlistens.length ] = f ) : ( this._unlistens[ opt_type ] = [ f ] ); } else { delete f.once; - // f.kill === X.Callback._kill && f.kill(); _list.splice( i, 1 ); if( !_list.length ){ - raw = this._rawObject || this._ie4getRawNode && this._ie4getRawNode(); + raw = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); delete list[ opt_type ]; - if( empty = X.isEmptyObject( list ) ) delete this._listeners; + if( empty = X_Object_isEmpty( list ) ) delete this._listeners; if( raw && '' + parseFloat( opt_type ) !== '' + opt_type ){ // 数字イベントの除外 - X_EventDispatcher_actualRemoveEvent( this, opt_type, raw, _list, !empty ); + X_EventDispatcher_removeEvent( this, opt_type, raw, _list, !empty ); }; }; }; return this; }; +/* + * X_EventDispatcher_systemListen から登録したイベントの解除 + */ +function X_EventDispatcher_systemUnlisten( that, type, opt_arg1, opt_arg2, opt_arg3 ){ + X_EventDispatcher_unlock = true; + that.unlisten( type, opt_arg1, opt_arg2, opt_arg3 ); + X_EventDispatcher_unlock = false; +}; + +function X_EventDispatcher_addEvent( that, type, raw, list ){ + var i; + X_EventDispatcher_lock || ( type = X_Event_Rename[ type ] || type ); + + if( X.Type.isArray( type ) ){ + for( i = type.length; i; ){ + X_EventDispatcher_systemListen( that, type[ --i ], X.emptyFunction ); + console.log( 'events fix > ' + type[ i ] ); + }; + } else { + X_EventDispatcher_actualAddEvent( that, type, raw, list ); + }; +}; var X_EventDispatcher_actualAddEvent = // Days on the Moon DOM Events とブラウザの実装 // http://nanto.asablo.jp/blog/2007/03/23/1339502 // Safari 2 では関数オブジェクトしか EventListener として使えませんが、Safari のナイトリービルドでは handleEvent メソッドを持つオブジェクトも EventListener として使えるようです。 - X_EventDispatcher_temp.EVENT_W3C /* && ( X.UA.WebKit < 525.13 || X.UA.Opera7 || X.UA.NetFront < 4 ) */ ? // Safari3- + X_UA_EVENT.W3C /* && ( X_UA.WebKit < 525.13 || X_UA.Opera7 || X_UA.NetFront < 4 ) */ ? // Safari3- (function( that, type, raw, list ){ - var i; - - type = X.Dom.Event.Rename[ type ] || type; - - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualAddEvent( that, type[ --i ], raw, list ); - }; + if( that._isXHR && X_UA.Opera < 12 ){ + // Opera11- の XHR は event オブジェクトが返らないため, eventType 毎に callback を指定する addEventListener もない + raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] ); + } else + if( that._isSilverlight ){ + list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); + list.sltoken = raw.AddEventListener( type, list.slcallback ); + } 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 { - that._handleEvent || ( that._handleEvent = X.Callback.create( that, X_EventDispatcher_actualHandleEvent ) ); - - if( that._isSilverlight ){ - list.slcallback = X.Callback.create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); - list.sltoken = raw.AddEventListener( type, list.slcallback ); - } else + console.log( 'event > ' + type ); + that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) ); + if( raw.addEventListener ){ raw.addEventListener( type, that._handleEvent, false ); } else { // Safari は Image, Opera7 は window raw[ 'on' + type ] = that._handleEvent; - }; + }; }; }) : - X_EventDispatcher_temp.EVENT_IE ? + X_UA_EVENT.IE ? (function( that, type, raw, list ){ - var i; - //if( type === 'load' && that._tag && X.Dom.Event._LOAD_FIX_TAGS[ that._tag ] ){ - // type = 'readystatechange'; - //}; if( that._isXHR ){ // ie8- の XHR は window.event が更新されないため, eventType 毎に callback を指定する - raw[ 'on' + type ] = X.Callback.create( that, X_EventDispatcher_dispatch, [ type ] ); - } else { - type = X.Dom.Event.Rename[ type ] || type; + raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] ); + } else + if( that._isSilverlight ){ + list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); + list.sltoken = raw.AddEventListener( type, list.slcallback ); + } else { + that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) ); - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualAddEvent( that, type[ --i ], raw, list ); - }; - } else{ - that._handleEvent || ( that._handleEvent = X.Callback.create( that, X_EventDispatcher_actualHandleEvent ) ); - - if( that._isSilverlight ){ - list.slcallback = X.Callback.create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); - list.sltoken = raw.AddEventListener( type, list.slcallback ); - } else - if( raw.attachEvent ){ - raw.attachEvent( 'on' + type, that._handleEvent ); - } else { - raw[ 'on' + type ] = that._handleEvent; - }; - }; + if( raw.attachEvent ){ + raw.attachEvent( 'on' + type, that._handleEvent ); + } else { + raw[ 'on' + type ] = that._handleEvent; + }; }; }) : (function( that, type, raw, list ){ - var i; - type = X.Dom.Event.Rename[ type ] || type; - - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualAddEvent( that, type[ --i ], raw, list ); - }; + if( that._isXHR ){ + // ie4 mobile は XHR をサポート! + raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] ); + } else + if( that._isSilverlight ){ + // DOM0 で Silverlight ってあるの? + list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); + list.sltoken = raw.AddEventListener( type, list.slcallback ); } else { - that._handleEvent || ( that._handleEvent = X.Callback.create( that, X_EventDispatcher_actualHandleEvent ) ); - - if( that._isSilverlight ){ - list.slcallback = X.Callback.create( that, X_EventDispatcher_sliverLightDispatch, [ type ] ); - list.sltoken = raw.AddEventListener( type, list.slcallback ); - } else { - raw[ 'on' + type ] = that._handleEvent; - }; + raw[ 'on' + type ] = that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) ); }; }); /* + * 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 */ @@ -546,97 +596,95 @@ function X_EventDispatcher_sliverLightDispatch( sender, e, type ){ return this.dispatch( type ); }; +function X_EventDispatcher_removeEvent( that, type, raw, list, skip ){ + var i; + X_EventDispatcher_unlock || ( type = X_Event_Rename[ type ] || type ); + + if( X.Type.isArray( type ) ){ + for( i = type.length; i; ){ + X_EventDispatcher_systemUnlisten( that, type[ --i ], X.emptyFunction ); + }; + } else { + X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip ); + }; +}; + var X_EventDispatcher_actualRemoveEvent = - X_EventDispatcher_temp.EVENT_W3C /*&& ( X.UA.WebKit < 525.13 || X.UA.Opera7 || X.UA.NetFront < 4 )*/ ? // Safari3- + X_UA_EVENT.W3C /*&& ( X_UA.WebKit < 525.13 || X_UA.Opera7 || X_UA.NetFront < 4 )*/ ? // Safari3- (function( that, type, raw, list, skip ){ - type = X.Dom.Event.Rename[ type ] || type; - - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualRemoveEvent( that, type[ --i ], raw, list, i ? true : skip ); - }; + if( that._isXHR && X_UA.Opera < 12 ){ + X_Callback_correct( raw[ 'on' + type ] ); + raw[ 'on' + type ] = ''; + } else + if( that._isSilverlight ){ + raw.RemoveEventListener( type, list.sltoken ); // token + 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( that._isSilverlight ){ - raw.RemoveEventListener( type, list.sltoken ); // token - X.Callback._correct( list.slcallback ); - delete list.sltoken; - delete list.slcallback; + + if( raw.addEventListener ){ + raw.removeEventListener( type, that._handleEvent, false ); } else { - if( raw.addEventListener ){ - raw.removeEventListener( type, that._handleEvent, false ); - } else { - raw[ 'on' + type ] = null; - }; - if( !skip ){ - X.Callback._correct( that._handleEvent ); - delete that._handleEvent; - }; + raw[ 'on' + type ] = null; + }; + if( !skip ){ + X_Callback_correct( that._handleEvent ); + delete that._handleEvent; }; }; }) : - X_EventDispatcher_temp.EVENT_IE ? + X_UA_EVENT.IE ? (function( that, type, raw, list, skip ){ - var i; - //if( type === 'load' && that._tag && X.Dom.Event._LOAD_FIX_TAGS[ that._tag ] ){ - // type = 'readystatechange'; - //}; if( that._isXHR ){ - X.Callback._correct( raw[ 'on' + type ] ); + X_Callback_correct( raw[ 'on' + type ] ); raw[ 'on' + type ] = X.emptyFunction; raw[ 'on' + type ] = ''; + } else + if( that._isSilverlight ){ + raw.RemoveEventListener( type, list.sltoken ); // token + X_Callback_correct( list.slcallback ); + delete list.sltoken; + delete list.slcallback; } else { - type = X.Dom.Event.Rename[ type ] || type; - - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualRemoveEvent( that, type[ --i ], raw, list, i ? true : skip ); - }; + if( raw.attachEvent ){ + raw.detachEvent( 'on' + type, that._handleEvent ); } else { - - if( that._isSilverlight ){ - raw.RemoveEventListener( type, list.sltoken ); // token - X.Callback._correct( list.slcallback ); - delete list.sltoken; - delete list.slcallback; - } else { - if( raw.attachEvent ){ - raw.detachEvent( 'on' + type, that._handleEvent ); - } else { - raw[ 'on' + type ] = X.emptyFunction; - raw[ 'on' + type ] = ''; - }; - - if( !skip ){ - X.Callback._correct( that._handleEvent ); - delete that._handleEvent; - }; - }; + raw[ 'on' + type ] = X.emptyFunction; + raw[ 'on' + type ] = ''; + }; + + if( !skip ){ + X_Callback_correct( that._handleEvent ); + delete that._handleEvent; }; }; }) : (function( that, type, raw, list, skip ){ - var i; - type = X.Dom.Event.Rename[ type ] || type; - if( X.Type.isArray( type ) ){ - for( i = type.length; i; ){ - X_EventDispatcher_actualRemoveEvent( that, type[ --i ], raw, list, i ? true : skip ); - }; + if( that._isXHR ){ + X_Callback_correct( raw[ 'on' + type ] ); + raw[ 'on' + type ] = X.emptyFunction; + raw[ 'on' + type ] = ''; + } else + if( that._isSilverlight ){ + raw.RemoveEventListener( type, list.sltoken ); // token + X_Callback_correct( list.slcallback ); + delete list.sltoken; + delete list.slcallback; } else { + raw[ 'on' + type ] = X.emptyFunction; + raw[ 'on' + type ] = ''; - if( that._isSilverlight ){ - raw.RemoveEventListener( type, list.sltoken ); // token - X.Callback._correct( list.slcallback ); - delete list.sltoken; - delete list.slcallback; - } else { - raw[ 'on' + type ] = X.emptyFunction; - raw[ 'on' + type ] = ''; - - if( !skip ){ - X.Callback._correct( that._handleEvent ); - delete that._handleEvent; - }; + if( !skip ){ + X_Callback_correct( that._handleEvent ); + delete that._handleEvent; }; }; }); @@ -646,7 +694,7 @@ var X_EventDispatcher_actualRemoveEvent = // Is this in regard to the Safari 1.x preventDefault bug on click/dblclick? // https://groups.google.com/forum/#!msg/comp.lang.javascript/uYEuCHjHxnw/yKoHtZJPa1QJ var X_EventDispatcher_actualHandleEvent = - X.UA.IE4 || X_EventDispatcher_temp.EVENT_IE ? // ie45678 EVENT_IE & EVENT_DOM0 for ie4 + X_UA_EVENT.IE4 || X_UA_EVENT.IE ? // ie45678 EVENT_IE & EVENT_DOM0 for ie4 (function(){ var ret; @@ -664,12 +712,12 @@ var X_EventDispatcher_actualHandleEvent = return event.returnValue = false; }; }) : - //X.Dom.EVENT_W3C & EVENT_DOM0 + //X_UA_EVENT.W3C & X_UA_EVENT.DOM0 (function( e ){ var ev = new X.Dom.Event( e, this ), - ret = X.Callback.NONE, + ret = X_Callback_NONE, i, l; - + //console.log( '>>>>>>>>>> ' + e.type ); // touch event -> pointer if( X.Type.isArray( ev ) ){ if( ev.length === 0 ){ @@ -677,6 +725,7 @@ var X_EventDispatcher_actualHandleEvent = ret = X.Callback.STOP_PROPAGATION | X.Callback.PREVENT_DEFAULT; } else { for( i = 0, l = ev.length; i < l; ++i ){ + console.log( 'handleEvent ' + ev[ i ].type ); ret |= this.dispatch( ev[ i ] ) || 0; }; }; @@ -690,7 +739,7 @@ var X_EventDispatcher_actualHandleEvent = if( ret & X.Callback.PREVENT_DEFAULT ){ this._tag === 'A' && this._rawObject.blur(); e.preventDefault(); - if( X.UA.WebKit < 525.13 ){ // Safari3- + if( X_UA.WebKit < 525.13 ){ // Safari3- if( e.type === 'click' || e.type === 'dbclick' ){ X_EventDispatcher_safariPreventDefault = true; }; @@ -699,7 +748,7 @@ var X_EventDispatcher_actualHandleEvent = }; }); -if( X.UA.WebKit < 525.13 ){ // Safari3- +if( X_UA.WebKit < 525.13 ){ // Safari3- document.documentElement.onclick = document.documentElement.ondbclick = function( e ){ if( X_EventDispatcher_safariPreventDefault ){ @@ -714,13 +763,15 @@ if( X.UA.WebKit < 525.13 ){ // Safari3- // 退避したイベントの復帰 function X_EventDispatcher_toggleAllEvents( that, add ){ var list = that._listeners, - raw = that._rawObject || that._ie4getRawNode && that._ie4getRawNode(), - f = add ? X_EventDispatcher_actualAddEvent : X_EventDispatcher_actualRemoveEvent, + raw = that._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( that ), + f = add ? X_EventDispatcher_addEvent : X_EventDispatcher_removeEvent, type; if( !list || !raw ) return; for( type in list ){ + //if( X_EMPTY_OBJECT[ type ] ) continue; // 数字イベントの除外 if( '' + parseFloat( type ) !== type ){ + // TODO type rename はここ f( that, type, raw, list[ type ], true ); }; };