OSDN Git Service

Fix the bug of X.NodeAnime.
[pettanr/clientJs.git] / 0.6.x / js / 01_core / 15_XEventDispatcher.js
index 5a5b17d..20701c3 100644 (file)
@@ -15,7 +15,7 @@
  * <dd>dispatch 中か?さらにインスタンス自身の dispatch がネストした場合、その深さを記憶します。\r
  * <dt>2:RESERVES Array\r
  * <dd>イベント発火中に listen() が呼ばれた場合に引数を蓄え、完了時(DISPATCHING===0)に再度 listen() するための一時ストアです。\r
- * <dt>3:UNLISTENS Array\r
+ * <dt>3:UNLISTENS Object\r
  * <dd>イベント発火中に unlisten() が呼ばれた場合に対象リスナを記憶し、リスナが呼ばれないようにします。完了時(DISPATCHING===0)に再度 unlisten() します。\r
  * <dt>4:KILL_RESERVED boolean\r
  * <dd>dispatch 中に kill() が呼ばれた場合に一旦 kill をキャンセルし、完了時(DISPATCHING===0)に再度 kill() するためのフラグです。\r
@@ -36,22 +36,27 @@ var X_LISTENERS_ACTUAL_HANDLER = 0,
 // ------------------------------------------------------------------------- //\r
 // ------------ local variables -------------------------------------------- //\r
 // ------------------------------------------------------------------------- //\r
+var X_EventDispatcher_EVENT_TARGET_OTHER        = 0,\r
+       X_EventDispatcher_EVENT_TARGET_XHR          = 1,\r
+       X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT = 2;\r
 \r
-var X_EventDispatcher_once       = false,\r
-       X_EventDispatcher_lock       = false,\r
-       X_EventDispatcher_unlock     = false,\r
-       X_EventDispatcher_needsIndex = false,\r
+var X_EventDispatcher_once         = false,\r
+       X_EventDispatcher_lock         = false,\r
+       X_EventDispatcher_unlock       = false,\r
+       X_EventDispatcher_needsIndex   = false,\r
        \r
        X_EventDispatcher_safariPreventDefault = false, // Safari3-\r
+\r
+       X_EventDispatcher_LAZY_TIMERS  = {},// Object.<number, X.EventDispatcher> number は timerID\r
        \r
-       /* @const */\r
-       X_EventDispatcher_EVENT_TARGET_OTHER        = 0,\r
-       /* @const */\r
-       X_EventDispatcher_EVENT_TARGET_XHR          = 1,\r
-       /* @const */\r
-       X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT = 2,\r
-       \r
-       X_EventDispatcher_LAZY_TIMERS = {}; // Object.<number, X.EventDispatcher> number は timerID\r
+       // iOS と MacOSX Iron36 で発生。連続してアニメーションが起こると、クロージャの束縛された obj へのアクセスに失敗する。Win では起きない?\r
+       // むしろ、MacOSX のブラウザ全般で起こる??\r
+       X_EventDispatcher_ANIME_EVENTS = ( X_UA[ 'WebKit' ] || X_UA[ 'Blink' ] ) && {\r
+               'transitionend'      : true, 'webkitTransitionEnd'      : true, 'mozTransitionEnd'    : true, 'oTransitionEnd' : true, 'otransitionEnd' : true,\r
+               'animationend'       : true, 'webkitAnimationEnd'       : true, 'oAnimationEnd'       : true,\r
+               'animationstart'     : true, 'webkitAnimationStart'     : true, 'oAnimationStart'     : true,\r
+               'animationiteration' : true, 'webkitAnimationIteration' : true, 'oAnimationIteration' : true\r
+       };\r
 \r
 // ------------------------------------------------------------------------- //\r
 // --- interface ----------------------------------------------------------- //\r
@@ -93,7 +98,9 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                {\r
 \r
                /**\r
-                * OTHER(Node,window,document,Image,Audio), XHR, Silverlight\r
+                * EventDispatcher がラップしている EventTarget オブジェクトのタイプです。<br>\r
+                * X_EventDispatcher_actualAddEvent で使用されます。<br>\r
+                * OTHER:0(node,window,document,Image,Audio), XHR:1, Silverlight:2\r
                 * @private\r
                 * @type {number}\r
                 */\r
@@ -101,7 +108,7 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                \r
                /**\r
                 * イベントリスナをイベント名文字列や数値(5以上、フレームワーク内で定義)をキーとするArrayで記憶します。<br>\r
-                * Arrayには、{kind:種類,context:コンテキスト(thisObject),func:コールバック関数,supplement:サプリメントする引数の配列} というハッシュ、または関数が蓄えられています。\r
+                * Arrayには、{cbKind:種類,context:コンテキスト(thisObject),func:コールバック関数,supplement:サプリメントする引数の配列} というハッシュ、または関数が蓄えられています。\r
                 * \r
                 * @private\r
                 * @type {__Listeners__}\r
@@ -176,8 +183,8 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                                if( !listeners || !( list = listeners[ opt_type ] ) ) return false;\r
                                if( opt_arg1 === undefined ) return X_EventDispatcher_needsIndex ? 0 : true;\r
                                \r
-                               // TODO callbackHash か?判定が不十分!\r
-                               if( opt_arg1.kind ){\r
+                               // TODO callbackHash か?判定が不十分! skipConvertion\r
+                               if( opt_arg1.cbKind ){\r
                                        cbHash = opt_arg1;\r
                                } else {\r
                                        cbHash = X_Closure_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
@@ -186,12 +193,13 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                                if( ( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ) && ( unlistens = unlistens[ opt_type ] ) ){\r
                                        for( i = unlistens.length; i; ){\r
                                                f = unlistens[ --i ];\r
-                                               if( f === cbHash || ( f.context === cbHash.context && f.func === cbHash.func && f.name === cbHash.name && f.supplement === cbHash.supplement && f.lock === lock ) ) return false;\r
+                                               if( f === cbHash || ( f.context === cbHash.context && f.func === cbHash.func && f.funcName === cbHash.funcName && f.supplement === cbHash.supplement && f.lock === lock ) ) return false;\r
                                        };\r
                                };\r
+                               \r
                                for( i = list.length; i; ){\r
                                        f = list[ --i ];\r
-                                       if( f === cbHash || ( f.context === cbHash.context && f.func === cbHash.func && f.name === cbHash.name && f.supplement === cbHash.supplement && f.lock === lock ) ){\r
+                                       if( f === cbHash || ( f.context === cbHash.context && f.func === cbHash.func && f.funcName === cbHash.funcName && f.supplement === cbHash.supplement && f.lock === lock ) ){\r
                                                // index を要求された場合、lock されていない、または unlock なら index を返す\r
                                                return X_EventDispatcher_needsIndex ? i : true;\r
                                        };\r
@@ -215,6 +223,9 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                                        e = delay;\r
                                        delay = 0;\r
                                };\r
+                               //{+dev\r
+                               delay === undefined && eval( 'throw "asyncDispatch で undefined イベントが指定されました"' );\r
+                               //}+dev\r
                                timerID = X_Timer_add( delay, 1, this, X_EventDispatcher_dispatch, [ e ] );\r
                                X_EventDispatcher_LAZY_TIMERS[ timerID ] = this;\r
                                return timerID;\r
@@ -255,22 +266,24 @@ function X_EventDispatcher_dispatch( e ){
        } else {\r
                listeners[ X_LISTENERS_DISPATCHING ] = 1;\r
        };\r
-       \r
-       // todo:\r
-       // type も保存\r
-       listeners[ X_LISTENERS_UNLISTENS ] = listeners[ X_LISTENERS_UNLISTENS ] || {};\r
-       unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
+\r
+       //listeners[ X_LISTENERS_UNLISTENS ] = listeners[ X_LISTENERS_UNLISTENS ] || {};\r
+       //unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
        \r
        for( i = 0; i < list.length; ++i ){\r
                f = list[ i ];\r
+               // TODO removed フラグは?\r
+               if( f.removed ) continue;\r
+               /*\r
                if( !unlistens ){\r
                        unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
                };\r
                if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
+               */\r
                \r
-               r = X_Closure_proxyCallback( f, args || ( args = [ e ] ) ) || 0;\r
+               r = X_Closure_proxyCallback( f, args || ( args = [ e ] ) );\r
                \r
-               if( f.once || r & X_CALLBACK_UN_LISTEN ){\r
+               if( f.once || ( r & X_CALLBACK_UN_LISTEN ) ){\r
                        // dispatch 中に unlisten が作られることがある\r
                        if( !unlistens ){\r
                                unlistens = listeners[ X_LISTENERS_UNLISTENS ] || ( listeners[ X_LISTENERS_UNLISTENS ] = {} );\r
@@ -360,10 +373,6 @@ function X_EventDispatcher_dispatch( e ){
  * this[ 'listen' ]( [ 'open', 'close', 'ready' ], onUpdate );\r
  * \r
  * @alias EventDispatcher.prototype.listen\r
- * @param {string|number|Array.<string,number>} type 配列を指定した場合、複数のイベントタイプに対して同じコールバックを登録する。\r
- * @param {listener|function|Array} [opt_arg1=]\r
- * @param {function|Array} [opt_arg2=]\r
- * @param {Array} [opt_arg3=] コールバック時の引数を配列に入れる。引数がひとつでも配列を使用する。省略した場合引数なし。\r
  * @return {EventDispatcher} チェインメソッド\r
  */\r
 function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){\r
@@ -413,13 +422,10 @@ function X_EventDispatcher_systemListen( that, type, opt_arg1, opt_arg2, opt_arg
 };\r
 \r
 /**\r
- * イベントリスナの解除を行う。登録時と同じ引数を与える必要がある。kill() ですべてのイベントが解除されるので、途中で解除されるイベント以外は kill() に任せてしまってよい。\r
+ * イベントリスナの解除を行う。登録時と同じ引数を与える必要がある。kill() で自信に登録されたすべてのイベントが解除されるので、途中で解除されるイベント以外は kill() に任せてしまってよい。<br>\r
+ * 他人に登録したイベントを解除せずに kill するのは NG。\r
  * @alias EventDispatcher.prototype.unlisten\r
  * @return {EventDispatcher}\r
- * @param {string|number|Array.<string,number>} opt_type イベントID, イベント名、またはその配列\r
- * @param {listener|function|Array} opt_arg1\r
- * @param {function|Array} opt_arg2\r
- * @param {Array} opt_arg3\r
  */\r
 function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){\r
        var listeners = this[ '_listeners' ],\r
@@ -455,13 +461,17 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){
 \r
        f = ( list = listeners[ opt_type ] )[ i ];\r
        \r
-       if( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ){\r
+       if( listeners[ X_LISTENERS_DISPATCHING ] ){\r
+               unlistens = listeners[ X_LISTENERS_UNLISTENS ] || ( listeners[ X_LISTENERS_UNLISTENS ] = {} );\r
                // _unlistens に入っている callbackHash は、lock のチェックは済んでいる\r
                ( unlistens = unlistens[ opt_type ] ) ?\r
                        ( unlistens[ unlistens.length ] = f ) :\r
                        ( listeners[ X_LISTENERS_UNLISTENS ][ opt_type ] = [ f ] );\r
+               f.removed = true;\r
        } else {\r
-               delete f.once;\r
+               //delete f.once;\r
+               X_Object_clear( f );\r
+               \r
                if( list.length !== 1 ){\r
                        list.splice( i, 1 );\r
                } else {\r
@@ -514,6 +524,7 @@ function X_EventDispatcher_unlistenAll( that ){
 \r
 function X_EventDispatcher_actualAddEvent( that, type, raw, list ){\r
        var i, f;\r
+\r
        X_EventDispatcher_lock || ( type = X_Event_Rename[ type ] || type );\r
        \r
        if( X_Type_isArray( type ) ){\r
@@ -542,13 +553,7 @@ function X_EventDispatcher_actualAddEvent( that, type, raw, list ){
                                        };\r
 \r
                                default :\r
-                                       // iOS と MacOSX Iron36 で発生。連続してアニメーションが起こると、クロージャの束縛された obj へのアクセスに失敗する。Win では起きない?\r
-                                       // むしろ、MacOSX のブラウザ全般で起こる??\r
-                                       if( ( X_UA[ 'WebKit' ] || X_UA[ 'Blink' ] ) &&\r
-                                               ( type === 'webkitTransitionEnd' || type === 'transitionend' ||\r
-                                                 type === 'animationend'        || type === 'webkitAnimationEnd' ||\r
-                                                 type === 'animationstart'      || type === 'webkitAnimationStart' ||\r
-                                                 type === 'animationiteration'  || type === 'webkitAnimationIteration' ) ){\r
+                                       if( X_EventDispatcher_ANIME_EVENTS && X_EventDispatcher_ANIME_EVENTS[ type ] ){\r
                                                raw.addEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false );\r
                                        } else {\r
                                                f = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Closure_create( that, X_EventDispatcher_actualHandleEvent ) );\r
@@ -627,6 +632,7 @@ function X_EventDispatcher_sliverLightDispatch( sender, e, type ){
 \r
 function X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip ){\r
        var i;\r
+\r
        X_EventDispatcher_unlock || ( type = X_Event_Rename[ type ] || type );\r
        \r
        if( X_Type_isArray( type ) ){\r
@@ -652,13 +658,9 @@ function X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip ){
                                        };\r
 \r
                                default :\r
-                                       if( ( X_UA[ 'WebKit' ] || X_UA[ 'Blink' ] ) &&\r
-                                               ( type === 'webkitTransitionEnd' || type === 'transitionend' ||\r
-                                                 type === 'animationend'        || type === 'webkitAnimationEnd' ||\r
-                                                 type === 'animationstart'      || type === 'webkitAnimationStart' ||\r
-                                                 type === 'animationiteration'  || type === 'webkitAnimationIteration' ) ){\r
+                                       if( X_EventDispatcher_ANIME_EVENTS && X_EventDispatcher_ANIME_EVENTS[ type ] ){\r
                                                raw.removeEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false );\r
-                                       } else                  \r
+                                       } else\r
                                        if( raw.addEventListener ){\r
                                                raw.removeEventListener( type, that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ], false );\r
                                        } else {\r
@@ -731,6 +733,9 @@ function X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip ){
 \r
 \r
 // TODO ブラウザからの呼び出しの最後に登録された関数を呼び出す機能(例えば画面の更新)\r
+var X_EventDispatcher_CURRENT_EVENTS    = [];\r
+var X_EventDispatcher_ignoreActualEvent;\r
+var X_EventDispatcher_rawEvent;\r
 \r
 // handleEvent を拡張可能にするために、クロージャに移動した\r
 // Is this in regard to the Safari 1.x preventDefault bug on click/dblclick?\r
@@ -738,24 +743,56 @@ function X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip ){
 var X_EventDispatcher_actualHandleEvent =\r
        X_UA_EVENT.IE4 || X_UA_EVENT.IE ? // ie45678 EVENT_IE & EVENT_DOM0 for ie4\r
                (function(){\r
-                       var e = event, ret;\r
+                       var e   = event,\r
+                               elm = this[ '_rawObject' ],\r
+                               ev, ret;\r
+                       \r
+                       if( X_EventDispatcher_ignoreActualEvent ){\r
+                               e.cancelBubble = true;\r
+                               return;\r
+                       };\r
+                       \r
+                       X_EventDispatcher_rawEvent = e;\r
                        \r
-                       ret = this[ 'dispatch' ]( new X_DomEvent( e, this, this[ '_rawObject' ] ) );\r
+                       ev = new X_DomEvent( e, this, elm );\r
+\r
+                       X_EventDispatcher_CURRENT_EVENTS[ X_EventDispatcher_CURRENT_EVENTS.length ] = ev;\r
+                       \r
+                       ret = this[ 'dispatch' ]( ev );\r
+\r
+                       if( X_EventDispatcher_rawEvent === e ) X_EventDispatcher_rawEvent = null;\r
+\r
+                       --X_EventDispatcher_CURRENT_EVENTS.length;\r
 \r
                        if( ret & X_CALLBACK_STOP_PROPAGATION ){\r
                                e.cancelBubble = true;\r
                        };\r
+                       \r
+                       if( !X_EventDispatcher_CURRENT_EVENTS.length ) ExecuteAtEnd_onEnd();\r
+                       \r
                        if( ret & X_CALLBACK_PREVENT_DEFAULT ){\r
-                               this[ '_tag' ] === 'A' && this[ '_rawObject' ].blur();\r
+                               X_EventDispatcher_ignoreActualEvent = true;\r
+                               this[ '_tag' ] === 'A' && elm.blur(); // おかしくない??\r
+                               X_EventDispatcher_ignoreActualEvent = false;\r
                                return e.returnValue = false;\r
                        };\r
                }) :\r
        //X_UA_EVENT.W3C || X_UA_EVENT.DOM0\r
                (function( e ){\r
-                       var ev  = new X_DomEvent( e, this ),\r
-                               ret = X_CALLBACK_NONE,\r
-                               i, l;\r
-                       //console.log( '>>>>>>>>>> ' + e.type );\r
+                       var ret = X_CALLBACK_NONE,\r
+                               elm = this[ '_rawObject' ],\r
+                               ev, i, l;\r
+                       \r
+                       if( X_EventDispatcher_ignoreActualEvent ){\r
+                               e.stopPropagation();\r
+                               return;\r
+                       };\r
+\r
+                       X_EventDispatcher_rawEvent = e;\r
+                       \r
+                       ev  = new X_DomEvent( e, this );\r
+                       X_EventDispatcher_CURRENT_EVENTS[ X_EventDispatcher_CURRENT_EVENTS.length ] = ev;\r
+\r
                        // touch event -> pointer\r
                        if( X_Type_isArray( ev ) ){\r
                                if( ev.length === 0 ){\r
@@ -771,11 +808,20 @@ var X_EventDispatcher_actualHandleEvent =
                                ret = this[ 'dispatch' ]( ev );\r
                        };\r
                        \r
+                       if( X_EventDispatcher_rawEvent === e ) X_EventDispatcher_rawEvent = null;\r
+                       \r
+                       --X_EventDispatcher_CURRENT_EVENTS.length;\r
+                       \r
+                       if( !X_EventDispatcher_CURRENT_EVENTS.length ) ExecuteAtEnd_onEnd();\r
+                       \r
                        if( ret & X_CALLBACK_STOP_PROPAGATION ){\r
                                e.stopPropagation();\r
                        };\r
                        if( ret & X_CALLBACK_PREVENT_DEFAULT ){\r
-                               this[ '_tag' ] === 'A' && this[ '_rawObject' ].blur();\r
+                               X_EventDispatcher_ignoreActualEvent = true;\r
+                               this[ '_tag' ] === 'A' && elm.blur();\r
+                               X_EventDispatcher_ignoreActualEvent = false;\r
+                               \r
                                e.preventDefault();\r
                                if( X_UA[ 'WebKit' ] < 525.13 ){ // Safari3-\r
                                        if( e.type === 'click' || e.type === 'dbclick' ){\r