From 7d0af2a7f884c146a48cd544a1f9ad44b82c0c1f Mon Sep 17 00:00:00 2001 From: itozyun Date: Thu, 18 Jun 2015 05:04:47 +0900 Subject: [PATCH] Version 0.6.163, fix __ClassBase__.kill(). --- 0.6.x/js/01_core/13_XClass.js | 67 ++++++++++++++++++++++++--------- 0.6.x/js/01_core/15_XEventDispatcher.js | 14 +++---- 0.6.x/js/06_net/00_XNet.js | 52 +++++++++++-------------- 0.6.x/js/06_net/10_XOAuth2.js | 22 +++++++---- 4 files changed, 92 insertions(+), 63 deletions(-) diff --git a/0.6.x/js/01_core/13_XClass.js b/0.6.x/js/01_core/13_XClass.js index ebe050b..2923520 100644 --- a/0.6.x/js/01_core/13_XClass.js +++ b/0.6.x/js/01_core/13_XClass.js @@ -24,8 +24,9 @@ var X_Class_CALL_SUPER_STACK = [], X_Class_traits = null, X_Class_useObjectCreate = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf + // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を辞めると動作,,, X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && !!X_emptyFunction.prototype.__proto__, - // Opera Mobile 12.10 Android11 IS01 でクラスのメンバが欠落する問題に遭遇。__proto__ を辞めると動作,,, + X_Class_SEAL_KILLING = [], X_Class_CommonMethods = /** @lends __ClassBase__.prototype */ @@ -34,41 +35,71 @@ X_Class_CommonMethods = * 全ての動的メンバを削除して、インスタンスを破棄する。
* インスタンスが X.EventDispatcher とそのサブクラスの場合、次の動作をする。 *
    - *
  1. X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X_Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。 + *
  2. X.Event.BEFORE_KILL_INSTANCE を発火する。戻り値のビットフラグに X_Callback.PREVENT_DEFAULT が立つ場合、破棄をキャンセルし X.Event.KILL_INSTANCE_CANCELED を発火する。この間に kill() が呼ばれても無視される。 *
  3. 破棄に進む場合は、X.Event.KILL_INSTANCE を発火する。 *
  4. dispatch 中は、インスタンスの全ての dispatch が終了するまで実際の破棄を待つ。 *
  5. 実際の破棄では、インスタンスのメンバの削除に加えて全てのイベントリスナを解除する。 */ // TODO kill したインスタンスのイベントが残っていないか?これは開発用のみ 'kill' : function(){ - var def, listeners, p; + var listeners, flag, p, timers, def; - // TODO kill 中の kill の呼び出しを防ぐ, 破棄済のインスタンスへの kill + // TODO 破棄済のインスタンスへの kill if( this[ 'instanceOf' ]( X_EventDispatcher ) ){ - if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){ - this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED ); - return; - }; - listeners = this[ '_listeners' ]; - - if( listeners && listeners[ X_LISTENERS_DISPATCHING ] ){ - listeners[ X_LISTENERS_KILL_RESERVED ] = true; - return; + + // SEAL のタイミングは、イベント中なので listeners が存在する + if( listeners && X_Class_SEAL_KILLING.length && X_Class_SEAL_KILLING.indexOf( this ) !== -1 ) return; + + // listeners がない場合、イベントの登録がないため、BEFORE_KILL_INSTANCE は呼ばれない。 + // KILL_RESERVED == true の場合、BEFORE_KILL_INSTANCE は呼ばれない。 + if( listeners && !listeners[ X_LISTENERS_KILL_RESERVED ] && listeners[ X_EVENT_BEFORE_KILL_INSTANCE ] ){ + X_Class_SEAL_KILLING[ X_Class_SEAL_KILLING.length ] = this; + + if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){ + this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED ); + // BEFORE_KILL_INSTANCE, KILL_INSTANCE_CANCELED 内で kill() しても PREVENT_DEFAULT の場合はこれを無視する。 + flag = true; + }; + + X_Class_SEAL_KILLING.length === 1 ? + ( X_Class_SEAL_KILLING.length = 0 ) : + X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING.indexOf( this ), 1 ); + + if( flag ) return; }; + if( listeners = this[ '_listeners' ] ){// unlisten 等で listeners が破棄されている場合があるので取り直し。 + if( listeners[ X_LISTENERS_DISPATCHING ] ){ + listeners[ X_LISTENERS_KILL_RESERVED ] = true; + return; + }; + + if( listeners[ X_EVENT_KILL_INSTANCE ] ){ + X_Class_SEAL_KILLING[ X_Class_SEAL_KILLING.length ] = this; + + listeners[ X_LISTENERS_KILL_RESERVED ] = false; + this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE ); + + X_Class_SEAL_KILLING.length === 1 ? + ( X_Class_SEAL_KILLING.length = 0 ) : + X_Class_SEAL_KILLING.splice( X_Class_SEAL_KILLING.indexOf( this ), 1 ); + }; + + X_EventDispatcher_unlistenAll( this ); + }; + + timers = X_EventDispatcher_LAZY_TIMERS; + // asyncDispatch の削除 - for( p in X_EventDispatcher_LAZY_TIMERS ){ - if( X_EventDispatcher_LAZY_TIMERS[ p ] === this ){ + for( p in timers ){ + if( timers[ p ] === this ){ // delete X_EventDispatcher_LAZY_TIMERS[ p ]; コレ不要 X_Timer_remove( p ); }; }; - - this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE ); - listeners && X_EventDispatcher_unlistenAll( this ); }; X_Object_clear( this ); diff --git a/0.6.x/js/01_core/15_XEventDispatcher.js b/0.6.x/js/01_core/15_XEventDispatcher.js index 4ee48a8..22142fe 100644 --- a/0.6.x/js/01_core/15_XEventDispatcher.js +++ b/0.6.x/js/01_core/15_XEventDispatcher.js @@ -244,7 +244,7 @@ function X_EventDispatcher_dispatch( e ){ var listeners = this[ '_listeners' ], ret = X_Callback_NONE, type = e[ 'type' ], - list, unlistens, i, l, args, f, r, sysOnly, timerID; + list, unlistens, i, l, args, f, r, sysOnly, timerID, k; if( !listeners || !( list = listeners[ type || e ] ) ) return X_Callback_NONE; @@ -315,14 +315,14 @@ 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( k in unlistens ){ + //if( X_EMPTY_OBJECT[ k ] ) continue; + list = unlistens[ k ]; for( i = list.length; i; ){ - this[ 'unlisten' ]( type, list[ --i ] ); + this[ 'unlisten' ]( k, list[ --i ] ); }; list.length = 0; - delete unlistens[ type ]; + delete unlistens[ k ]; }; X_EventDispatcher_unlock = false; }; @@ -429,6 +429,7 @@ function X_EventDispatcher_systemListen( that, type, opt_arg1, opt_arg2, opt_arg function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){ var listeners = this[ '_listeners' ], list, reserves, unlistens, i, f, raw, k, empty; + if( !listeners ) return this; if( X_Type_isArray( opt_type ) ){ @@ -810,7 +811,6 @@ function X_EventDispatcher_toggleAllEvents( that, add ){ if( !list || !raw ) return; for( type in list ){ //if( X_EMPTY_OBJECT[ type ] ) continue; - //if( type <= X_LISTENERS_KILL_RESERVED ) continue; // 数字イベントの除外 if( !X_String_isNumberString( type ) ){ // TODO type rename はここ diff --git a/0.6.x/js/06_net/00_XNet.js b/0.6.x/js/06_net/00_XNet.js index 3073cf3..6bacae7 100644 --- a/0.6.x/js/06_net/00_XNet.js +++ b/0.6.x/js/06_net/00_XNet.js @@ -7,17 +7,16 @@ /** - *

    busy() メソッドだけを持つ。通信用のプロパティは X.Pair によって隠蔽される。 + *

    state() メソッドだけを持つ。通信用のプロパティは X.Pair によって隠蔽されています。 *

    通信のキャンセル

    - *

    kill() で通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。SUCCESS, ERROR, TIMEOUT イベント以降は kill() は無視される。 + *

    kill() で通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。 *

    イベント

    *
    *
    X.Event.PROGRESS
    通信進行状況 *
    X.Event.SUCCESS
    通信成功 - *
    X.Event.ERROR
    通信エラー - *
    X.Event.TIMEOUT
    通信タイムアウト - *
    X.Event.CANCELED
    通信のユーザー、プログラムによるキャンセル - *
    X.Event.COMPLETE
    通信完了。SUCCESS, ERROR, TIMEOUT, CANCELED 後に発生。 + *
    X.Event.ERROR
    通信エラー タイムアウトの場合、e.timeout == true で分かる。 + *
    X.Event.CANCELED
    通信のユーザー、プログラムによるキャンセル。SUCCESS, ERROR, COMPLETE 後に kill()してもCANCELEDは呼ばれません。 + *
    X.Event.COMPLETE
    通信完了。SUCCESS, ERROR, CANCELED 後に発生。 *
    *

    X.Net インスタンスは COMPLETE 後に自動で破棄される。 *

    必須プロパティ

    @@ -91,9 +90,6 @@ X[ 'Net' ] = X_EventDispatcher[ 'inherits' ]( 'X.Net', X_Class.NONE, { - 'netType' : '', - 'netName' : '', - 'netVersion' : 0, 'Constructor' : function( urlOrObject, opt_options ){ var opt, url, type, auth; @@ -186,17 +182,9 @@ X[ 'Net' ] = X_EventDispatcher[ 'inherits' ]( X_NET_QUEUE_LIST[ X_NET_QUEUE_LIST.length ] = this; !X_NET_currentQueue && X_NET_shiftQueue(); }, - - /** - * 現在通信中か?調べる。false の場合、通信の順番待ちか、通信が終了している。 - * @alias Net.prototype.busy - */ - 'busy' : function(){ - return this === X_NET_currentQueue && X_NET_currentWrapper._busy; - }, /** - * 現在の状態。1:待機中 2:通信中 3:通信完了フェーズ + * 現在の状態。1:順番待ち, 2:通信中, 3:通信完了フェーズ * @alias Net.prototype.state */ 'state' : function(){ @@ -247,9 +235,12 @@ function X_NET_proxyDispatch( e ){ switch( e.type ){ case X_EVENT_KILL_INSTANCE : if( this === X_NET_currentQueue && X_NET_completePhase ){ - X_Pair_release( this ); + if( X_NET_completePhase === 1 ) + this[ 'unlisten' ]( X_EVENT_COMPLETE, X_NET_proxyDispatch ) + [ 'dispatch' ]( X_EVENT_COMPLETE ); X_NET_shiftQueue(); - X_NET_completePhase = false; + X_Pair_release( this ); + X_NET_completePhase = 0; } else if( this === X_NET_currentQueue ){ X_NET_currentWrapper.cancel(); @@ -275,22 +266,20 @@ function X_NET_proxyDispatch( e ){ if( e.status === 401 ){ if( auth = X_Pair_get( this )[ 'auth' ] ){ X_Pair_get( auth ).onAuthError( auth, e ); + // TODO 破棄しないで待機。 }; }; case X_EVENT_SUCCESS : - X_NET_completePhase = true; - this - [ 'listenOnce' ]( X_EVENT_COMPLETE, X_NET_proxyDispatch ) - [ 'asyncDispatch' ]( e ); - this[ 'asyncDispatch' ]( 32, { type : X_EVENT_COMPLETE, 'lastEventType' : e.type } ); + X_NET_completePhase = 1; + this[ 'listenOnce' ]( X_EVENT_COMPLETE, X_NET_proxyDispatch ) + [ 'asyncDispatch' ]( 32, { type : X_EVENT_COMPLETE, 'lastEventType' : e.type } ); + + this[ 'asyncDispatch' ]( e ); break; case X_EVENT_COMPLETE : - X_Pair_release( this ); - X_NET_shiftQueue(); - X_NET_completePhase = false; - this[ 'unlisten' ]( X_EVENT_KILL_INSTANCE, X_NET_proxyDispatch ); + X_NET_completePhase = 2; this[ 'kill' ](); break; }; @@ -301,9 +290,11 @@ function X_NET_shiftQueue(){ if( X_NET_currentQueue ){ if( X_NET_currentWrapper._busy ) return; + X_NET_currentWrapper [ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_ERROR ], X_NET_currentQueue, X_NET_proxyDispatch ) .reset(); + X_NET_currentQueue = X_NET_currentWrapper = X_NET_currentData = null; }; @@ -337,7 +328,8 @@ function X_NET_shiftQueue(){ case 1 : case 2 : if( !( auth[ 'dispatch' ]( X_EVENT_NEED_AUTH ) & X_Callback_PREVENT_DEFAULT ) ){ - authSettings.lazyReq = X_NET_currentQueue; + authSettings.lazyRequests = authSettings.lazyRequests || []; + authSettings.lazyRequests.indexOf( X_NET_currentQueue ) === -1 && authSettings.lazyRequests.push( X_NET_currentQueue ); }; X_NET_currentQueue = null; X_NET_shiftQueue(); diff --git a/0.6.x/js/06_net/10_XOAuth2.js b/0.6.x/js/06_net/10_XOAuth2.js index a3967ff..c4f92b2 100644 --- a/0.6.x/js/06_net/10_XOAuth2.js +++ b/0.6.x/js/06_net/10_XOAuth2.js @@ -16,6 +16,7 @@ var X_NET_OAUTH2_detection = new Function( 'w', 'try{return w.location.sear * * original : * oauth2.js , + * https://github.com/ox-it/javascript-oauth2/blob/master/oauth2/oauth2.js * * @alias X.OAuth2 * @class OAuth2 サービスを定義し接続状況をモニタする。適宜にトークンのアップデートなどを行う @@ -30,6 +31,7 @@ oauth2 = X.OAuth2({ 'redirectURI' : X.URL.cleanup( document.location.href ), // 専用の軽量ページを用意してもよいが、現在のアドレスでも可能 'scopes' : [ 'https://www.googleapis.com/auth/blogger' ], 'refreshMargin' : 300000, + // canuse 'authorizeWindowWidth' : 500, 'authorizeWindowHeight' : 500 }).listen( [ X.Event.NEED_AUTH, X.Event.CANCELED, X.Event.SUCCESS, X.Event.ERROR, X.Event.PROGRESS ], updateOAuth2State ); @@ -182,7 +184,7 @@ X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ]( 'Accept' : 'application/json', 'Content-Type' : 'application/x-www-form-urlencoded' }, - 'test' : 'gadget' + 'test' : 'gadget' // canuse } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_Net_OAuth2_responceHandler ); this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to refresh token.' } ); @@ -294,6 +296,12 @@ function X_Net_OAuth2_responceHandler( e ){ }; pair.oauth2State = 4; + + if( pair.lazyRequests && pair.lazyRequests.length ){ + X_NET_QUEUE_LIST.push.apply( X_NET_QUEUE_LIST, pair.lazyRequests ); + pair.lazyRequests.length = 0; + }; + this[ 'asyncDispatch' ]( { type : X_EVENT_SUCCESS, message : isRefresh ? 'Refresh access token success.' : 'Get new access token success.' } ); break; @@ -322,17 +330,16 @@ function X_Net_OAuth2_responceHandler( e ){ function X_NET_OAUTH2_onXHR401Error( oauth2, e ){ var pair = this, headers = e[ 'headers' ], - xhr, bearerParams, headersExposed = false; + bearerParams, headersExposed = false; if( X_OAuth2_getAuthMechanism( oauth2 ) !== 'param' ){ - xhr = X_NET_currentWrapper[ '_rawObject' ]; - headersExposed = !X_Net_XHR_createXDR || !!headers; // this is a hack for Firefox and IE + headersExposed = !X_NET_currentWrapper.isXDR || !!headers; // this is a hack for Firefox and IE bearerParams = headersExposed && ( headers[ 'WWW-Authenticate' ] || headers[ 'www-authenticate' ] ); X_Type_isArray( bearerParams ) && ( bearerParams = bearerParams.join( '\n' ) ); }; // http://d.hatena.ne.jp/ritou/20110402/1301679908 - if ( bearerParams && bearerParams.indexOf( ' error=' ) === -1 ) { + if ( bearerParams && bearerParams.indexOf( ' error=' ) === -1 ) { // bearerParams.error == undefined pair.oauth2State = 0; oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); } else @@ -341,7 +348,6 @@ function X_NET_OAUTH2_onXHR401Error( oauth2, e ){ pair.oauth2State = 3; oauth2[ 'refreshToken' ](); } else { - //if (!headersExposed && !X_OAuth2_getRefreshToken( oauth2 )) { X_OAuth2_removeAccessToken( oauth2 ); // It doesn't work any more. pair.oauth2State = 0; oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH ); @@ -365,12 +371,12 @@ function X_NET_OAUTH2_updateRequest( oauth2, request ){ }; function X_OAuth2_getAccessToken( that ){ return X_OAuth2_updateLocalStorage( '', that, 'accessToken' ); } -function X_OAuth2_getRefreshToken( that){ return X_OAuth2_updateLocalStorage( '', that, 'refreshToken' ); } +function X_OAuth2_getRefreshToken( that ){ return X_OAuth2_updateLocalStorage( '', that, 'refreshToken' ); } function X_OAuth2_getAccessTokenExpiry( that ){ return parseInt( X_OAuth2_updateLocalStorage( '', that, 'tokenExpiry' ) ) || 0; } function X_OAuth2_getAuthMechanism( that ){ // TODO use gadget | flash ... // IE's XDomainRequest doesn't support sending headers, so don't try. - return X_Net_XHR_createXDR ? 'param' : X_OAuth2_updateLocalStorage( '', that, 'AuthMechanism' ); + return ( X_NET_currentWrapper === X_NET_XHRWrapper ) && X_Net_XHR_createXDR ? 'param' : X_OAuth2_updateLocalStorage( '', that, 'AuthMechanism' ); } function X_OAuth2_setAccessToken( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'accessToken' , value); } function X_OAuth2_setRefreshToken( that, value ){ X_OAuth2_updateLocalStorage( '+', that, 'refreshToken', value); } -- 2.11.0