From: itozyun Date: Mon, 17 Aug 2015 08:53:10 +0000 (+0900) Subject: fix X.UA & X.Audio, add X.KB. X-Git-Url: http://git.osdn.jp/view?p=pettanr%2FclientJs.git;a=commitdiff_plain;h=475df4df9670f042764a99c80d7716e994d28033 fix X.UA & X.Audio, add X.KB. --- diff --git a/0.6.x/js/01_core/02_XUA.js b/0.6.x/js/01_core/02_XUA.js index 3ce9532..94799e4 100644 --- a/0.6.x/js/01_core/02_XUA.js +++ b/0.6.x/js/01_core/02_XUA.js @@ -193,7 +193,23 @@ var X_UA = X[ 'UA' ] = {}, */ X_UA[ 'Linux' ] = true; - if( ( i = dua.indexOf( 'Android' ) ) !== -1 ){ + if( dua.indexOf( 'Android ' ) !== -1 ){ + v = dua.split( 'Android ' )[ 1 ].split( '.' ); + /** + * @alias X.UA.AndroidMajor + * @type {number} + */ + X_UA[ 'AndroidMajor' ] = parseFloat( v[ 0 ] ) || 0; + /** + * @alias X.UA.AndroidMinor + * @type {number} + */ + X_UA[ 'AndroidMinor' ] = parseFloat( v[ 1 ] ) || 0; + /** + * @alias X.UA.AndroidPatch + * @type {number} + */ + X_UA[ 'AndroidPatch' ] = parseFloat( v[ 2 ] ) || 0; /** * Firefox で Version が取れない! * http://bizmakoto.jp/bizid/articles/1207/31/news004.html @@ -205,7 +221,7 @@ var X_UA = X[ 'UA' ] = {}, * @alias X.UA.Android * @type {number} */ - X_UA[ 'Android' ] = parseFloat( dua.substr( i + 8 ) ) || 0.1; + X_UA[ 'Android' ] = X_UA[ 'AndroidMajor' ] + X_UA[ 'AndroidMinor' ] / 10; console.log( '>> Android : ' + X_UA[ 'Android' ] ); }; }; @@ -486,57 +502,42 @@ var X_UA = X[ 'UA' ] = {}, console.log( '>> Gecko : ' + X_UA[ 'Gecko' ] ); }; - // TODO Blink - if( window.chrome ){ - /** - * @alias X.UA.Blink - * @type {number} - */ - X_UA[ 'Blink' ] = tv; - console.log( '>>Blink : ' + X_UA[ 'Blink' ] ); - - } else - if( dav.indexOf( 'Konqueror' ) !== -1 ){ - /** - * @alias X.UA.Khtml - * @type {number} - */ - X_UA[ 'Khtml' ] = tv; - console.log( '>>Khtml : ' + X_UA[ 'Khtml' ] ); - - } else - if( ( i = dua.indexOf( 'Android ' ) ) !== -1 ){ + if( dua.indexOf( 'Linux; U; Android ' ) !== -1 || dua.indexOf( 'Linux; Android ' ) !== -1 ){ /** * Android 標準ブラウザ * @alias X.UA.AndroidBrowser * @type {number} */ - X_UA[ 'AndroidBrowser' ] = i = parseFloat( dua.substr( i + 8 ) ) || 0.1; + X_UA[ 'AndroidBrowser' ] = X_UA[ 'Android' ]; + + v = X_UA[ 'AndroidMajor' ]; + /** * @alias X.UA.AndroidBrowser1 * @type {boolean} */ - X_UA[ 'AndroidBrowser1' ] = 1 <= i && i < 2; + X_UA[ 'AndroidBrowser1' ] = v === 1; /** * @alias X.UA.AndroidBrowser2 * @type {boolean} */ - X_UA[ 'AndroidBrowser2' ] = 2 <= i && i < 3; + X_UA[ 'AndroidBrowser2' ] = v === 2; /** * @alias X.UA.AndroidBrowser3 * @type {boolean} */ - X_UA[ 'AndroidBrowser3' ] = 3 <= i && i < 4; + X_UA[ 'AndroidBrowser3' ] = v === 3; /** * @alias X.UA.AndroidBrowser4 * @type {boolean} */ - X_UA[ 'AndroidBrowser4' ] = 4 <= i && i < 5; + X_UA[ 'AndroidBrowser4' ] = v === 4; /** * @alias X.UA.AndroidBrowser5 * @type {boolean} */ - X_UA[ 'AndroidBrowser5' ] = 5 <= i && i < 6; + X_UA[ 'AndroidBrowser5' ] = v === 5; + console.log( '>> AndroidBrowser : ' + X_UA[ 'Android' ] ); i = parseFloat(dua.split('WebKit\/')[1]); @@ -547,7 +548,39 @@ var X_UA = X[ 'UA' ] = {}, X_UA[ 'AndroidWebkit' ] = i; //alert( 'AudioSprite調査:Android標準ブラウザ Webkit Version ' + i ); + if( window.chrome ){ + //X_UA[ 'Blink' ] = X_UA[ 'AndroidChromeBrowser' ] = tv; + } else + if( v = parseFloat(dua.split('Chrome\/')[1]) ){ + X_UA[ 'Chrome' ] = X_UA[ 'AndroidChromeBrowser' ] = v; + }; + + //if( window[ 'webkitRequestFileSystem' ] ) alert( 'requestFileSystem' ); + + //alert( 'html.style.WebkitAppearance:' + ( document.documentElement.style[ 'WebkitAppearance' ] === undefined ) + ' win.chrome:' + !!( window.chrome ) ); + + } else + + // TODO Blink + if( window.chrome ){ // Android3.1 の標準ブラウザで .chrome がいた、、、 + /** + * @alias X.UA.Blink + * @type {number} + */ + X_UA[ 'Blink' ] = tv; + console.log( '>>Blink : ' + X_UA[ 'Blink' ] ); + + } else + if( dav.indexOf( 'Konqueror' ) !== -1 ){ + /** + * @alias X.UA.Khtml + * @type {number} + */ + X_UA[ 'Khtml' ] = tv; + console.log( '>>Khtml : ' + X_UA[ 'Khtml' ] ); + } else + if( i = parseFloat(dua.split('WebKit\/')[1]) ){ /** * @alias X.UA.WebKit diff --git a/0.6.x/js/01_core/07_XNumber.js b/0.6.x/js/01_core/07_XNumber.js new file mode 100644 index 0000000..0d59f64 --- /dev/null +++ b/0.6.x/js/01_core/07_XNumber.js @@ -0,0 +1,37 @@ +/** + * Number に関する関数を集めたものです。 + * @namespace X.Number + * @alias X.Number + */ +X[ 'Number' ] = { + 'conpareVersion' : X_Number_conpareVersion +}; + +/** + * X.X.X という形式のバージョン文字列同志の比較 + * -1 v1 < v2 + * 0 v1 = v2 + * 1 v1 > v2 + * @alias X.Number.copy + * @param {Number} ary コピー元のオブジェクトです。 + * @return {Number} + */ + function X_Number_conpareVersion( v1, v2 ){ + var i = 0, + l, n1, n2; + + v1 = v1.split( '.' ); + v2 = v2.split( '.' ); + + l = Math.min( v1.length, v2.length ); + + for( ; i < l; ++i ){ + n1 = parseFloat( v1[ i ] ); + n2 = parseFloat( v2[ i ] ); + if( n1 !== n2 ){ + return n1 > n2 ? 1 : -1; + }; + }; + if( v1.length === v2.length ) return 0; + return v1.length > v2.length ? 1 : -1; +}; diff --git a/0.6.x/js/01_core/11_Callback.js b/0.6.x/js/01_core/11_XCallback.js similarity index 63% rename from 0.6.x/js/01_core/11_Callback.js rename to 0.6.x/js/01_core/11_XCallback.js index a7d69fb..2eff2e9 100644 --- a/0.6.x/js/01_core/11_Callback.js +++ b/0.6.x/js/01_core/11_XCallback.js @@ -3,23 +3,14 @@ // ------------ local variables -------------------------------------------- // // ------------------------------------------------------------------------- // -var /** @const */ - X_Callback_NONE = 0, - /** @const */ - X_Callback_UN_LISTEN = 1, - /** @const */ - X_Callback_STOP_PROPAGATION = 2, - /** @const */ - X_Callback_STOP_NOW = 4 | 2, // 同一階層のリスナーのキャンセル(上位へもキャンセル) - /** @const */ - X_Callback_PREVENT_DEFAULT = 8, // 結果動作のキャンセル, - /** @const */ - X_Callback_CAPTURE_POINTER = 16, - /** @const */ - X_Callback_RELEASE_POINTER = 32, - - /** @const */ - X_Callback_SYS_CANCEL = 64 | 4 | 2; +var X_CALLBACK_NONE = 0, + X_CALLBACK_UN_LISTEN = 1, + X_CALLBACK_STOP_PROPAGATION = 2, + X_CALLBACK_STOP_NOW = 4 | 2, + X_CALLBACK_PREVENT_DEFAULT = 8, + X_CALLBACK_CAPTURE_POINTER = 16, + X_CALLBACK_RELEASE_POINTER = 32, + X_CALLBACK_SYS_CANCEL = 64 | 4 | 2; /** * X.Timer と X.EventDispatcher からのコールバックの返り値を定義。 @@ -30,45 +21,39 @@ X[ 'Callback' ] = { * このコールバックでは返り値による操作は無い。 * @alias X.Callback.NONE */ - 'NONE' : X_Callback_NONE, + 'NONE' : X_CALLBACK_NONE, /** * X.Timer, X.EventDispatcher のコールバックでタイマーやイベントリスナの解除に使用。 * @alias X.Callback.UN_LISTEN */ - 'UN_LISTEN' : X_Callback_UN_LISTEN, + 'UN_LISTEN' : X_CALLBACK_UN_LISTEN, /** * 上位階層へのイベント伝播のキャンセル。DOM イベントのコールバックの戻り値に指定すると e.stopPropagation() が呼ばれる。 * @alias X.Callback.STOP_PROPAGATION */ - 'STOP_PROPAGATION' : X_Callback_STOP_PROPAGATION, + 'STOP_PROPAGATION' : X_CALLBACK_STOP_PROPAGATION, /** * 以降のイベントのディスパッチを中断する。STOP_PROPAGATION との違いは、次に控えているコールバックもキャンセルされる点。但し system によって追加されたイベントはキャンセルされない。 * @alias X.Callback.STOP_NOW */ - 'STOP_NOW' : X_Callback_STOP_NOW, + 'STOP_NOW' : X_CALLBACK_STOP_NOW, /** * DOM イベントのコールバックの戻り値に指定すると e.preventDefault() が呼ばれる。 * またフレームワーク内で定義されたデフォルト動作の回避にも使用される。 * @alias X.Callback.PREVENT_DEFAULT */ - 'PREVENT_DEFAULT' : X_Callback_PREVENT_DEFAULT, + 'PREVENT_DEFAULT' : X_CALLBACK_PREVENT_DEFAULT, /** * X.UI の uinode でポインターイベントの戻り値に指定すると、以降のポインターベントを独占する。 * @alias X.Callback.CAPTURE_POINTER */ - 'CAPTURE_POINTER' : X_Callback_CAPTURE_POINTER, + 'CAPTURE_POINTER' : X_CALLBACK_CAPTURE_POINTER, /** * X.UI の uinode でポインターイベントの戻り値に指定すると、以降のポインターベントを独占を解除する。 * @alias X.Callback.RELEASE_POINTER */ - 'RELEASE_POINTER' : X_Callback_RELEASE_POINTER + 'RELEASE_POINTER' : X_CALLBACK_RELEASE_POINTER }; -X_TEMP.onSystemReady.push( function( sys ){ - sys.monitor( X_Callback_monitor ); - sys.gc( X_Callback_gc ); -}); - - console.log( 'X.Core.Callback' ); diff --git a/0.6.x/js/01_core/12_Closure.js b/0.6.x/js/01_core/12_XClosure.js similarity index 65% rename from 0.6.x/js/01_core/12_Closure.js rename to 0.6.x/js/01_core/12_XClosure.js index 5d90451..82ff4a9 100644 --- a/0.6.x/js/01_core/12_Closure.js +++ b/0.6.x/js/01_core/12_XClosure.js @@ -1,31 +1,27 @@ var - X_Callback_LIVE_LIST = [], + X_CLOSURE_LIVE_LIST = [], - X_Callback_POOL_LIST = [], + X_CLOSURE_POOL_LIST = [], - X_Closure_COMMAND_BACK = X_Callback_LIVE_LIST, + X_Closure_COMMAND_BACK = X_CLOSURE_LIVE_LIST, - X_Closure_COMMAND_DROP = X_Callback_POOL_LIST, + X_Closure_COMMAND_DROP = X_CLOSURE_POOL_LIST, - /** @const */ - X_Callback_THIS_FUNC = 1, - /** @const */ - X_Callback_HANDLEEVENT = 2, - /** @const */ - X_Callback_FUNC_ONLY = 3, - /** @const */ - X_Callback_THIS_FUNCNAME = 4; + X_CLOSURE_THIS_FUNC = 1, + X_CLOSURE_HANDLEEVENT = 2, + X_CLOSURE_FUNC_ONLY = 3, + X_CLOSURE_THIS_FUNCNAME = 4; /** *

クロージャに関するポリシーと再利用可能クロージャについて次の記事をご覧ください。 * 再利用できるクロージャを使ったWebアプリケーション開発 * *

再利用可能クロージャの作成
- * X_Callback_create() で再利用可能なクロージャの作成。次のパターンで呼び出します。
+ * X_Closure_create() で再利用可能なクロージャの作成。次のパターンで呼び出します。
* 最大で三つの引数を並べる一連のパターンは、 EventDispatcher.listen unlisten, listening や X.Timer.add, once でも使われますので、ここでよく目を通しておきます。 * *
再利用可能クロージャの破棄と再利用
- * X_Callback_correct() によってクロージャは回収され再利用に備えます。
+ * X_Closure_correct() によってクロージャは回収され再利用に備えます。
* 実は、クロージャが束縛するのは、this コンテキストやコールバック関数といった、そのものではなく、それらを一定のルールで格納したハッシュです。
* このハッシュはクロージャに与えた後も、適宜に取得が可能です。このハッシュのメンバーを書き換えることで、クロージャの this コンテキストやコールバック関数を書き換えています。 * @@ -41,7 +37,7 @@ var __CallbackHash__ = * コールバックの種類を表す数値。 this + function, this.handleEvent, function only がある。 * @type {number} */ - kind : X_Callback_THIS_FUNC, + kind : X_CLOSURE_THIS_FUNC, /** * コールバック。 * @type {funciton|undefined} @@ -66,21 +62,21 @@ var __CallbackHash__ = * __CallbackHash__ の情報を元に、コールバックを実施するプロキシ。 * @type {Function} */ - proxy : X_Callback_proxyCallback + proxy : X_Closure_proxyCallback }; // ------------------------------------------------------------------------- // // --- implements ---------------------------------------------------------- // // ------------------------------------------------------------------------- // -function X_Callback_create( thisObject, opt_callback, opt_args /* [ listener || ( context + function ) || function ][ args... ] */ ){ - var obj = X_Callback_classifyCallbackArgs( thisObject, opt_callback, opt_args ), +function X_Closure_create( thisObject, opt_callback, opt_args /* [ listener || ( context + function ) || function ][ args... ] */ ){ + var obj = X_Closure_classifyCallbackArgs( thisObject, opt_callback, opt_args ), l, ret, _obj; if( !obj.kind ) return obj; - if( l = X_Callback_POOL_LIST.length ){ - ret = X_Callback_POOL_LIST[ l - 1 ]; --X_Callback_POOL_LIST.length; // ret = X_Callback_POOL_LIST.pop(); + if( l = X_CLOSURE_POOL_LIST.length ){ + ret = X_CLOSURE_POOL_LIST[ l - 1 ]; --X_CLOSURE_POOL_LIST.length; // ret = X_CLOSURE_POOL_LIST.pop(); _obj = ret( X_Closure_COMMAND_BACK ); _obj.kind = obj.kind; @@ -88,52 +84,52 @@ function X_Callback_create( thisObject, opt_callback, opt_args /* [ listener || _obj.func = obj.func; _obj.context = obj.context; _obj.supplement = obj.supplement; - _obj.proxy = X_Callback_proxyCallback; + _obj.proxy = X_Closure_proxyCallback; } else { - ret = X_Callback_actualClosure( obj ); - obj.proxy = X_Callback_proxyCallback; + ret = X_Closure_actualClosure( obj ); + obj.proxy = X_Closure_proxyCallback; }; - X_Callback_LIVE_LIST[ X_Callback_LIVE_LIST.length ] = ret; + X_CLOSURE_LIVE_LIST[ X_CLOSURE_LIVE_LIST.length ] = ret; return ret; }; -function X_Callback_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){ +function X_Closure_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){ var obj; if( X_Type_isObject( arg1 ) && X_Type_isFunction( arg2 ) ){ - obj = { context : arg1, func : arg2, kind : X_Callback_THIS_FUNC }; + obj = { context : arg1, func : arg2, kind : X_CLOSURE_THIS_FUNC }; } else if( X_Type_isObject( arg1 ) ){ if( arg2 && X_Type_isString( arg2 ) ){ - obj = { context : arg1, name : arg2, kind : X_Callback_THIS_FUNCNAME }; + obj = { context : arg1, name : arg2, kind : X_CLOSURE_THIS_FUNCNAME }; } else { - obj = { context : arg1, kind : X_Callback_HANDLEEVENT }; + obj = { context : arg1, kind : X_CLOSURE_HANDLEEVENT }; arg3 = arg2; }; } else if( X_Type_isFunction( arg1 ) ){ arg3 = arg2; if( alt_context ){ - obj = { context : alt_context, func : arg1, kind : X_Callback_THIS_FUNC }; + obj = { context : alt_context, func : arg1, kind : X_CLOSURE_THIS_FUNC }; } else { - obj = { func : arg1, kind : X_Callback_FUNC_ONLY }; + obj = { func : arg1, kind : X_CLOSURE_FUNC_ONLY }; }; } else if( X_Type_isFunction( arg2 ) ){ - //console.log( 'X_Callback_classifyCallbackArgs : arg1 が ' + arg1 + 'です' ); ie4 で error + //console.log( 'X_Closure_classifyCallbackArgs : arg1 が ' + arg1 + 'です' ); ie4 で error if( alt_context ){ - obj = { context : alt_context, func : arg2, kind : X_Callback_THIS_FUNC }; + obj = { context : alt_context, func : arg2, kind : X_CLOSURE_THIS_FUNC }; } else { - obj = { func : arg2, kind : X_Callback_FUNC_ONLY }; + obj = { func : arg2, kind : X_CLOSURE_FUNC_ONLY }; }; } else if( alt_context && X_Type_isString( arg1 ) ){ arg3 = arg2; - obj = { context : alt_context, name : arg1, kind : X_Callback_THIS_FUNCNAME }; + obj = { context : alt_context, name : arg1, kind : X_CLOSURE_THIS_FUNCNAME }; } else if( alt_context ){ - obj = { context : alt_context, kind : X_Callback_HANDLEEVENT }; + obj = { context : alt_context, kind : X_CLOSURE_HANDLEEVENT }; arg3 = arg1; } else { console.log( '不正 ' + arg1 ); @@ -147,14 +143,14 @@ function X_Callback_classifyCallbackArgs( arg1, arg2, arg3, alt_context ){ return ( obj.context || obj.supplement ) ? obj : arg1; }; -function X_Callback_actualClosure( obj ){ +function X_Closure_actualClosure( obj ){ return function(){ if( arguments[ 0 ] === X_Closure_COMMAND_BACK ) return obj; if( arguments[ 0 ] !== X_Closure_COMMAND_DROP ) return obj.proxy && obj.proxy( obj, arguments ); }; }; -function X_Callback_proxyCallback( xfunc, _args ){ +function X_Closure_proxyCallback( xfunc, _args ){ var args = _args || [], thisObj = xfunc.context, func = xfunc.func, @@ -177,12 +173,12 @@ function X_Callback_proxyCallback( xfunc, _args ){ switch( xfunc.kind ){ - case X_Callback_THIS_FUNC : + case X_CLOSURE_THIS_FUNC : return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args ); - case X_Callback_THIS_FUNCNAME : + case X_CLOSURE_THIS_FUNCNAME : funcName = xfunc.name; - case X_Callback_HANDLEEVENT : + case X_CLOSURE_HANDLEEVENT : funcName = funcName || 'handleEvent'; temp = thisObj[ funcName ]; if( X_Type_isFunction( temp ) ){ @@ -199,40 +195,49 @@ function X_Callback_proxyCallback( xfunc, _args ){ }; return args.length === 0 ? func.call( thisObj ) : func.apply( thisObj, args );*/ - case X_Callback_FUNC_ONLY : + case X_CLOSURE_FUNC_ONLY : return args.length === 0 ? func() : args.length === 1 ? func( args[ 0 ] ) : func.apply( null, args ); }; - return X_Callback_NONE; + return X_CALLBACK_NONE; }; -function X_Callback_correct( f ){ - var i = X_Callback_LIVE_LIST.indexOf( f ), +function X_Closure_correct( f ){ + var i = X_CLOSURE_LIVE_LIST.indexOf( f ), obj; if( i !== -1 ){ - X_Callback_LIVE_LIST.splice( i, 1 ); - X_Callback_POOL_LIST[ X_Callback_POOL_LIST.length ] = f; + X_CLOSURE_LIVE_LIST.splice( i, 1 ); + X_CLOSURE_POOL_LIST[ X_CLOSURE_POOL_LIST.length ] = f; obj = f( X_Closure_COMMAND_BACK ); + /* delete obj.kind; if( obj.name ) delete obj.name; if( obj.func ) delete obj.func; if( obj.context ) delete obj.context; if( obj.supplement ) delete obj.supplement; - delete obj.proxy; + delete obj.proxy; */ + X_Object_clear( obj ); return true; }; return false; }; -function X_Callback_monitor(){ +function X_Closure_monitor(){ return { - 'Callback:Live' : X_Callback_LIVE_LIST.length, - 'Callback:Pool' : X_Callback_POOL_LIST.length + 'Callback:Live' : X_CLOSURE_LIVE_LIST.length, + 'Callback:Pool' : X_CLOSURE_POOL_LIST.length }; }; -function X_Callback_gc(){ - X_Callback_POOL_LIST.length = 0; // ? -}; \ No newline at end of file +function X_Closure_gc(){ + X_CLOSURE_POOL_LIST.length = 0; // ? +}; + +X_TEMP.onSystemReady.push( function( sys ){ + sys.monitor( X_Closure_monitor ); + sys.gc( X_Closure_gc ); +}); + + diff --git a/0.6.x/js/01_core/21_XViewPort.js b/0.6.x/js/01_core/21_XViewPort.js index b4f53b8..2349dde 100644 --- a/0.6.x/js/01_core/21_XViewPort.js +++ b/0.6.x/js/01_core/21_XViewPort.js @@ -35,7 +35,7 @@ X_ViewPort = X_Class_override( { 'handleEvent' : function( e ){ - var href, i, name, active = false; + var href, i, name, active = false, xnode; switch( e.type ){ case 'beforeunload' : @@ -57,30 +57,43 @@ X_ViewPort = X_Class_override( break; case 'visibilitychange' : + console.log( e.type + ':' + document[ 'hidden' ] ); X_ViewPort[ 'dispatch' ]( ( X_ViewPort_active = document[ 'hidden' ] ) ? X_EVENT_VIEW_DEACTIVATE : X_EVENT_VIEW_ACTIVATE ); break; + case 'msvisibilitychange' : + X_ViewPort[ 'dispatch' ]( ( X_ViewPort_active = document[ 'msHidden' ] ) ? X_EVENT_VIEW_DEACTIVATE : X_EVENT_VIEW_ACTIVATE ); + break; case 'mozvisibilitychange' : X_ViewPort[ 'dispatch' ]( ( X_ViewPort_active = document[ 'mozHidden' ] ) ? X_EVENT_VIEW_DEACTIVATE : X_EVENT_VIEW_ACTIVATE ); break; case 'webkitvisibilitychange' : + console.log( e.type + ':' + document[ 'webkitHidden' ] ); X_ViewPort[ 'dispatch' ]( ( X_ViewPort_active = document[ 'webkitHidden' ] ) ? X_EVENT_VIEW_DEACTIVATE : X_EVENT_VIEW_ACTIVATE ); break; - case 'blur' : case 'focusout' : - case 'pagehide' : + if( X_UA[ 'IE' ] < 9 ){ + xnode = X_Node_getXNode( document.activeElement ); + if( xnode ){ + xnode[ 'listen' ]( [ 'focus', 'blur' ], X_ViewPort_detectFocusForIE ); + //break; + }; + if( X_ViewPort_activeTimerID ){ + X_ViewPort_activeTimerID = X_Timer_remove( X_ViewPort_activeTimerID ); + }; + X_ViewPort_activeTimerID = X_Timer_once( 16, X_ViewPort_changeFocus ); + return X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_STOP_PROPAGATION; + }; + case 'pagehide' : active = true; case 'focus' : - case 'focusin' : case 'pageshow' : + case 'focusin' : if( X_ViewPort_active === active ){ X_ViewPort_active = !active; - if( X_ViewPort_activeTimerID ){ - X_ViewPort_activeTimerID = X_Timer_remove( X_ViewPort_activeTimerID ); - } else { - X_ViewPort_activeTimerID = X_Timer_once( 1, X_ViewPort_changeFocus ); - }; + console.log( e.type + ':' + X_ViewPort_active ); + X_ViewPort[ 'dispatch' ]( active ? X_EVENT_VIEW_DEACTIVATE : X_EVENT_VIEW_ACTIVATE ); }; break; }; @@ -89,6 +102,22 @@ X_ViewPort = X_Class_override( } ); +function X_ViewPort_detectFocusForIE( e ){ + console.log( 'iefix! ' + e.type + ':' + this.attr( 'tag' ) + ' isActive?:' + ( this[ '_rawObject' ] === document.activeElement ) ); + + X_ViewPort_active = e.type === 'focus'; + + if( this[ '_rawObject' ] !== document.activeElement ){ + this[ 'unlisten' ]( X_ViewPort_active ? 'blur' : 'focus', X_ViewPort_detectFocusForIE ); + }; + if( X_ViewPort_activeTimerID ){ + X_ViewPort_activeTimerID = X_Timer_remove( X_ViewPort_activeTimerID ); + }; + X_ViewPort_activeTimerID = X_Timer_once( 16, X_ViewPort_changeFocus ); + + return X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_STOP_PROPAGATION; +}; + function X_ViewPort_changeFocus(){ X_ViewPort[ 'dispatch' ]( X_ViewPort_active ? X_EVENT_VIEW_ACTIVATE : X_EVENT_VIEW_DEACTIVATE ); X_ViewPort_activeTimerID = 0; @@ -106,14 +135,24 @@ X[ 'ViewPort' ] = { * @alias X.ViewPort.listen */ 'listen' : function( type, arg1, arg2, arg3 ){ + var f; + if( type <= X_ViewPort_readyState ){ /* * X_EVENT_XDOM_READY 以後に listen した場合の対策 */ X_ViewPort[ 'asyncDispatch' ]( type ); }; - // ie8-では keydown -> documentへ - type && arg1 && X_ViewPort[ 'listen' ]( type, arg1, arg2, arg3 ); + + f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + if( !f.kind ){ + X_ViewPort[ 'listen' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort[ 'listen' ]( type, this, f.func, f.supplement ); + } else { + X_ViewPort[ 'listen' ]( type, arg1, arg2, arg3 ); + }; return X[ 'ViewPort' ]; }, @@ -122,13 +161,24 @@ X[ 'ViewPort' ] = { * @alias X.ViewPort.listenOnce */ 'listenOnce' : function( type, arg1, arg2, arg3 ){ + var f; + if( type <= X_ViewPort_readyState ){ /* * X.Event.XDOM_READY 以後に listen した場合の対策 */ X_ViewPort[ 'asyncDispatch' ]( type ); }; - type && arg1 && X_ViewPort[ 'listenOnce' ]( type, arg1, arg2, arg3 ); + + f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + if( !f.kind ){ + X_ViewPort[ 'listenOnce' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort[ 'listenOnce' ]( type, this, f.func, f.supplement ); + } else { + X_ViewPort[ 'listenOnce' ]( type, arg1, arg2, arg3 ); + }; return X[ 'ViewPort' ]; }, @@ -137,7 +187,16 @@ X[ 'ViewPort' ] = { * @alias X.ViewPort.unlisten */ 'unlisten' : function( type, arg1, arg2, arg3 ){ - type && arg1 && X_ViewPort[ 'unlisten' ]( type, arg1, arg2, arg3 ); + var f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + + if( !f.kind ){ + X_ViewPort[ 'unlisten' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort[ 'unlisten' ]( type, this, f.func, f.supplement ); + } else { + X_ViewPort[ 'unlisten' ]( type, arg1, arg2, arg3 ); + }; return X[ 'ViewPort' ]; }, @@ -146,6 +205,14 @@ X[ 'ViewPort' ] = { * @alias X.ViewPort.listening */ 'listening' : function( type, arg1, arg2, arg3 ){ + var f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + + if( !f.kind ){ + return X_ViewPort[ 'listening' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + return X_ViewPort[ 'listening' ]( type, this, f.func, f.supplement ); + }; return X_ViewPort[ 'listening' ]( type, arg1, arg2, arg3 ); }, @@ -180,8 +247,8 @@ X[ 'ViewPort' ] = { //(((t = document.documentElement) || (t = document.body.parentNode)) && typeof t.ScrollLeft == 'number' ? t : document.body).ScrollLeft; //(((t = document.documentElement) || (t = document.body.parentNode)) && typeof t.ScrollTop == 'number' ? t : document.body).ScrollTop - /** - * + /* + * TODO X.Doc へ * @alias X.ViewPort.getDocumentSize */ 'getDocumentSize' : function(){ @@ -448,24 +515,36 @@ X[ 'ViewPort' ] = { //ブラウザの戻るボタンで戻ったときに呼ばれるイベントとかキャッシュとかそこらへんのこと //http://d.hatena.ne.jp/koumiya/20080916/1221580149 +console.log( '------------------->' ); + + if( document[ 'webkitHidden' ] !== undefined ){ + console.log( '--> has webkitvisibilitychange' ); + X_EventDispatcher_systemListen( X_ViewPort_document, 'webkitvisibilitychange', X_ViewPort ); + } else if( document[ 'hidden' ] !== undefined ){// iOS 7+ + console.log( '--> has visibilitychange' ); X_EventDispatcher_systemListen( X_ViewPort_document, 'visibilitychange', X_ViewPort ); + document.onvisibilitychange = function(){ console.log( '!!!!!!!!!!!!!!!!' ) }; + } else + if( document[ 'msHidden' ] !== undefined ){ + X_EventDispatcher_systemListen( X_ViewPort_document, 'msvisibilitychange', X_ViewPort ); } else if( document[ 'mozHidden' ] !== undefined ){ X_EventDispatcher_systemListen( X_ViewPort_document, 'mozvisibilitychange', X_ViewPort ); - } else - if( document[ 'webkitHidden' ] !== undefined ){ - X_EventDispatcher_systemListen( X_ViewPort_document, 'webkitvisibilitychange', X_ViewPort ); - } else - if( X_UA[ 'iOS' ] && window[ 'onpageshow' ] !== undefined ){ + }; + + if( window[ 'onpageshow' ] !== undefined ){ + console.log( '-------------------> pageshow, pagehide' ); X_EventDispatcher_systemListen( X_ViewPort, [ 'pageshow', 'pagehide' ] ); - } else + }; + if( document[ 'onfocusin' ] !== undefined ){ + console.log( '-------------------> focusin, focusout' ); // https://github.com/ai/visibilityjs/blob/master/lib/visibility.fallback.js X_EventDispatcher_systemListen( X_ViewPort_document, [ 'focusin', 'focusout' ], X_ViewPort ); - } else { - X_EventDispatcher_systemListen( X_ViewPort, [ 'focus', 'blur' ] ); }; + + X_EventDispatcher_systemListen( X_ViewPort, [ 'focus', 'blur' ] ); return X_CALLBACK_UN_LISTEN; }; diff --git a/0.6.x/js/02_dom/00_XDoc.js b/0.6.x/js/02_dom/00_XDoc.js index 7232833..f4c88bb 100644 --- a/0.6.x/js/02_dom/00_XDoc.js +++ b/0.6.x/js/02_dom/00_XDoc.js @@ -9,13 +9,24 @@ X[ 'Doc' ] = { * @alias X.Doc.listen */ 'listen' : function( type, arg1, arg2, arg3 ){ + var f; + if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){ /* * X.Event.XDOM_READY 以後に listen した場合の対策 */ X_ViewPort_document[ 'asyncDispatch' ]( type ); }; - type && arg1 && X_ViewPort_document[ 'listen' ]( type, arg1, arg2, arg3 ); + + f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + if( !f.kind ){ + X_ViewPort_document[ 'listen' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort_document[ 'listen' ]( type, this, f.func, f.supplement ); + } else { + X_ViewPort_document[ 'listen' ]( type, arg1, arg2, arg3 ); + }; return X[ 'Doc' ]; }, @@ -24,13 +35,23 @@ X[ 'Doc' ] = { * @alias X.Doc.listenOnce */ 'listenOnce' : function( type, arg1, arg2, arg3 ){ + var f; + if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){ /* * X.Event.XDOM_READY 以後に listen した場合の対策 */ X_ViewPort_document[ 'asyncDispatch' ]( type ); }; - type && arg1 && X_ViewPort_document[ 'listenOnce' ]( type, arg1, arg2, arg3 ); + + f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + if( !f.kind ){ + X_ViewPort_document[ 'listenOnce' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort_document[ 'listenOnce' ]( type, this, f.func, f.supplement ); + }; + X_ViewPort_document[ 'listenOnce' ]( type, arg1, arg2, arg3 ); return X[ 'Doc' ]; }, @@ -39,7 +60,16 @@ X[ 'Doc' ] = { * @alias X.Doc.unlisten */ 'unlisten' : function( type, arg1, arg2, arg3 ){ - type && arg1 && X_ViewPort_document[ 'unlisten' ]( type, arg1, arg2, arg3 ); + var f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + + if( !f.kind ){ + X_ViewPort_document[ 'unlisten' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + X_ViewPort_document[ 'unlisten' ]( type, this, f.func, f.supplement ); + } else { + X_ViewPort_document[ 'unlisten' ]( type, arg1, arg2, arg3 ); + }; return X[ 'Doc' ]; }, @@ -48,6 +78,14 @@ X[ 'Doc' ] = { * @alias X.Doc.listening */ 'listening' : function( type, arg1, arg2, arg3 ){ + var f = X_Closure_classifyCallbackArgs( arg1, arg2, arg3 ); + + if( !f.kind ){ + return X_ViewPort_document[ 'listening' ]( type, this, arg1 ); + } else + if( f.kind === X_CLOSURE_FUNC_ONLY ){ + return X_ViewPort_document[ 'listening' ]( type, this, f.func, f.supplement ); + }; return X_ViewPort_document[ 'listening' ]( type, arg1, arg2, arg3 ); }, diff --git a/0.6.x/js/02_dom/03_XDomEvent.js b/0.6.x/js/02_dom/03_XDomEvent.js index 07886c0..391cdb9 100644 --- a/0.6.x/js/02_dom/03_XDomEvent.js +++ b/0.6.x/js/02_dom/03_XDomEvent.js @@ -213,11 +213,12 @@ if( !X_UA[ 'IE' ] || 9 <= X_UA[ 'IE' ] ){ } else { // Other - this[ 'keyCode' ] = e.keyCode || e.which; - this[ 'altKey' ] = e.altKey; - this[ 'ctrlKey' ] = e.ctrlKey; - this[ 'shiftKey' ] = e.shiftKey; - this[ 'metaKey' ] = e.metaKey; + this[ 'keyCode' ] = X_Type_isFinite( e.keyCode ) ? e.keyCode : X_Type_isFinite( e.charCode ) ? e.charCode : e.which; + this[ 'charCode' ] = X_Type_isFinite( e.charCode ) ? e.charCode : e.which; + this[ 'altKey' ] = e.altKey || !!( e.modifiers & 1 ); + this[ 'ctrlKey' ] = e.ctrlKey || !!( e.modifiers & 2 ); + this[ 'shiftKey' ] = e.shiftKey || !!( e.modifiers & 4 ); + this[ 'metaKey' ] = e.metaKey || !!( e.modifiers & 8 );; this[ 'button' ] = e.button !== undefined ? e.button : e.which !== undefined ? e.which - 1 : -1; @@ -272,9 +273,11 @@ if( !X_UA[ 'IE' ] || 9 <= X_UA[ 'IE' ] ){ if( this[ 'target' ] && !this[ 'target' ][ '_tag' ] ) this[ 'target' ] = this[ 'target' ].parent; // ie4 の fake Textnode がヒットしていないか? this[ 'currentTarget' ] = xnode; // xnode this[ 'relatedTarget' ] = X_Node_getXNode( e.formElement || e.toElement ); // xnode + this[ 'relatedTarget' ] && console.dir( 'relatide...' ); this[ 'eventPhase' ] = e.srcElement === element ? 2: 3; this[ 'keyCode' ] = e.keyCode; + this[ 'charCode' ] = e.keyCode; this[ 'altKey' ] = e.altKey; this[ 'ctrlKey' ] = e.ctrlKey; this[ 'shiftKey' ] = e.shiftKey; diff --git a/0.6.x/js/02_dom/20_XNode.js b/0.6.x/js/02_dom/20_XNode.js index de76d17..49ae640 100644 --- a/0.6.x/js/02_dom/20_XNode.js +++ b/0.6.x/js/02_dom/20_XNode.js @@ -1188,7 +1188,7 @@ function X_Node_call( name /*, opt_args... */ ){ }; return raw[ name ](); } else - if( X_Type_isUnknown( func ) ){ + if( X_Type_isUnknown( func ) || ( X_UA[ 'IE' ] < 9 && X_Type_isObject( func ) ) ){ // typeof func === unknown に対策 // http://la.ma.la/blog/diary_200509031529.htm if( l ){ diff --git a/0.6.x/js/06_net/01_XNetXHR.js b/0.6.x/js/06_net/01_XNetXHR.js index 785bcbf..d882cc1 100644 --- a/0.6.x/js/06_net/01_XNetXHR.js +++ b/0.6.x/js/06_net/01_XNetXHR.js @@ -74,7 +74,7 @@ var // Opera7.6+, Safari1.2+, khtml3.?+, Gecko0.9.7+ X_Net_XHR_msXMLVer = 0, X_Net_XHR_msXML = X_Net_XHR_createMSXML && X_Net_XHR_createMSXML( true ), - X_Net_XHR_neverReuse = X_UA[ 'IE' ] < 9 || X_UA[ 'iOS' ] || X_UA[ 'Android' ], // ie7,8 の xhr はリユース不可。msxml はリユース可能。 + X_Net_XHR_neverReuse = X_UA[ 'IE' ] < 9, // ie7,8 の xhr はリユース不可。msxml はリユース可能。 X_Net_XHR_TYPE_FLASH = 8, X_Net_XHR_TYPE_GADGET = 16; diff --git a/0.6.x/js/06_net/04_XNetImage.js b/0.6.x/js/06_net/04_XNetImage.js index da48845..c7d95d5 100644 --- a/0.6.x/js/06_net/04_XNetImage.js +++ b/0.6.x/js/06_net/04_XNetImage.js @@ -131,7 +131,6 @@ function X_NET_Image_handleEvent( e ){ case X_EVENT_KILL_INSTANCE : this.reset(); - !X_Net_Image_hasImage && this[ 'kill' ](); // if xnode break; }; }; diff --git a/0.6.x/js/07_audio/00_XAudio.js b/0.6.x/js/07_audio/00_XAudio.js index b12e679..4086c18 100644 --- a/0.6.x/js/07_audio/00_XAudio.js +++ b/0.6.x/js/07_audio/00_XAudio.js @@ -432,8 +432,10 @@ var X_Audio_AbstractAudioBackend = X_EventDispatcher[ 'inherits' ]( }; }; break; + case 'useVideo' : + break; default : - throw 'bad arg'; + throw ( 'bad arg! ' + k ); }; }; diff --git a/0.6.x/js/07_audio/01_XWebAudio.js b/0.6.x/js/07_audio/01_XWebAudio.js index dd90819..f1bc2bd 100644 --- a/0.6.x/js/07_audio/01_XWebAudio.js +++ b/0.6.x/js/07_audio/01_XWebAudio.js @@ -5,7 +5,8 @@ var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] ( window[ 'AudioContext' ] || window[ 'webkitAudioContext' ] ), X_Audio_BUFFER_LIST = [], X_Audio_WebAudioWrapper, - X_Audio_BufferLoader; + X_Audio_BufferLoader, + X_Audio_fpsFix; /* * iPhone 4s 以下、iPad2以下、iPad mini 1以下, iPod touch 4G 以下は不可 @@ -138,6 +139,16 @@ if( X_Audio_WebAudio_context ){ l = X_Audio_BUFFER_LIST.length, loader; + /* + * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1 + * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。 + * 描画レート(描画 FPS)が下がるとノイズが混ざり始め、レートを上げると再生結果が正常になるというもので、オーディオ処理が描画スレッドに巻き込まれているような動作を見せる。 + */ + if( X_UA[ 'Android' ] && X_UA[ 'Chrome' ] && !X_Audio_fpsFix ){ + X_Node_systemNode.create( 'div', { id : 'fps-slowdown-make-sound-noisy' } ); + X_Audio_fpsFix = true; + }; + for( ; i < l; ++i ){ loader = X_Audio_BUFFER_LIST[ i ]; if( loader.url === url ){ @@ -336,15 +347,6 @@ if( X_Audio_WebAudio_context ){ } ); - /* - * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1 - * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。 - * 描画レート(描画 FPS)が下がるとノイズが混ざり始め、レートを上げると再生結果が正常になるというもので、オーディオ処理が描画スレッドに巻き込まれているような動作を見せる。 - */ - if( X_UA[ 'Android' ] && X_UA[ 'Chrome' ] ){ - X_Node_systemNode.create( 'div', { id : 'fps-slowdown-make-sound-noisy' } ); - }; - X_Audio_BACKENDS.push( { backendName : 'Web Audio', diff --git a/0.6.x/js/07_audio/02_XHTMLAudio.js b/0.6.x/js/07_audio/02_XHTMLAudio.js index 3972841..dd71bde 100644 --- a/0.6.x/js/07_audio/02_XHTMLAudio.js +++ b/0.6.x/js/07_audio/02_XHTMLAudio.js @@ -8,14 +8,17 @@ var X_Audio_HTMLAudio_playTrigger = 6 <= X_UA[ 'iOS' ] ? 'loadeddata' : X_UA[ 'iOS' ] < 5 ? 'stalled' : X_UA[ 'iOS' ] ? 'suspend' : - X_UA[ 'AndroidBrowser2' ] || X_UA[ 'AndroidBrowser3' ] ? 'stalled' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,, - X_UA[ 'AndroidBrowser4' ] ? 'loadeddata' : + X_UA[ 'AndroidChromeBrowser' ] ? 'canplaythrough' : + // Android 2.3.5(SBM101SH) では stalled は発生しない,,, ので必ず loadeddata もチェックする + X_UA[ 'AndroidBrowser' ] ? 'stalled' : X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : //X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ? 'canplay' : 'loadeddata', //'canplay', X_Audio_HTMLAudioWrapper, X_Audio_constructor = window[ 'Audio' ] || window.HTMLAudioElement, X_Audio_rawAudio, + // onended イベント時に再生を継続する場合、audio.play() を呼ぶ必要がある + X_Audio_HTMLAudioWrapper_needPlayOnended = !X_UA[ 'AndroidChromeBrowser' ] && X_UA[ 'AndroidBrowser' ], // Opera Mobile 12 android4.4.4 & 2.3.5 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する X_Audio_HTMLAudioWrapper_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ], // || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), // Android1.6+MobileOpera12では無理っぽい、、、 @@ -30,9 +33,10 @@ var X_Audio_HTMLAudio_playTrigger = // opera11、10.54 WinXP はまとも、、、 // X_Audio_Sprite_handleEvent でも使用 X_Audio_HTMLAudioWrapper_ieMobile9Fix = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), - X_Audio_HTMLAudioWrapper_durationFix = ( !X_Audio_HTMLAudioWrapper_currentTimeFix && 12 <= X_UA[ 'Opera' ] ), + X_Audio_HTMLAudioWrapper_durationFix = ( !X_Audio_HTMLAudioWrapper_currentTimeFix && 12 <= X_UA[ 'Opera' ] ) || X_UA[ 'AndroidChromeBrowser' ], + + X_Audio_HTMLAudioWrapper_shortPlayFix = X_UA[ 'AndroidBrowser' ] && X_UA[ 'AndroidWebkit' ] <= 534.3, // Android 4.1.1 でも遭遇(ただしm4a, mp3は優秀, oggはシークが乱れる) - X_Audio_HTMLAudioWrapper_shortPlayFix = X_UA[ 'AndroidBrowser' ] && X_UA[ 'AndroidWebkit' ] <= 534.3, // Android 4.1.1 でも遭遇 X_Audio_codecs; @@ -43,6 +47,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ // https://html5experts.jp/miyuki-baba/3766/ // Chrome for Android31 で HE-AAC が低速再生されるバグ + // TODO Android4 標準ブラウザで ogg のシークが正しくない! if( X_Audio_rawAudio.canPlayType ){ X_Audio_codecs = { 'mp3' : X_Audio_rawAudio.canPlayType('audio/mpeg'), @@ -54,25 +59,41 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ 'mp4' : X_Audio_rawAudio.canPlayType('audio/x-mp4') + X_Audio_rawAudio.canPlayType('audio/mp4') + X_Audio_rawAudio.canPlayType('audio/aac'), 'weba' : X_Audio_rawAudio.canPlayType('audio/webm; codecs="vorbis"') }; - (function( k, v ){ + (function( X_Audio_codecs, k, v ){ for( k in X_Audio_codecs ){ - if( X_EMPTY_OBJECT[ k ] ) continue; + //if( X_EMPTY_OBJECT[ k ] ) continue; v = X_Audio_codecs[ k ]; - X_Audio_codecs[ k ] = v && v.split( 'no' ).join( '' ); - console.log( k + ' ' + X_Audio_codecs[ k ] ); + v = v && !!( v.split( 'no' ).join( '' ) ); + if( v ){ + console.log( k + ' ' + X_Audio_codecs[ k ] ); + X_Audio_codecs[ k ] = true; + } else { + delete X_Audio_codecs[ k ]; + }; }; - })(); + })( X_Audio_codecs ); } else { // iOS3.2.3 X_Audio_codecs = { - 'mp3' : X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ), + 'mp3' : X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ), 'ogg' : 5 <= X_UA[ 'Gecko' ] || X_UA[ 'Chrome' ] || X_UA[ 'Opera' ] , - 'wav' : X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ), + 'wav' : X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ), 'aac' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ], 'm4a' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ], 'mp4' : X_UA[ 'IE' ] || X_UA[ 'WebKit' ], 'weba' : 2 <= X_UA[ 'Gecko' ] || 10.6 <= X_UA[ 'Opera' ] // firefox4+(Gecko2+) }; + (function( X_Audio_codecs, k ){ + for( k in X_Audio_codecs ){ + //if( X_EMPTY_OBJECT[ k ] ) continue; + if( X_Audio_codecs[ k ] ){ + console.log( k + ' ' + X_Audio_codecs[ k ] ); + X_Audio_codecs[ k ] = true; + } else { + delete X_Audio_codecs[ k ]; + }; + }; + })( X_Audio_codecs ); }; X_Audio_HTMLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ]( @@ -87,12 +108,17 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ _lastCurrentTime : 0, _src : '', + isM4A : false, + shortPlayFix : 0, + 'Constructor' : function( target, source, option ){ var raw; this.target = target || this; this._closed = false; + this.isM4A = X_URL_getEXT( source ) === 'm4a'; + this.setState( option ); if( option[ 'useVideo' ] ){ @@ -169,7 +195,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ X_Audio_HTMLAudioWrapper_badOperaAndroid && alert( e.type ); // global に公開 - window[ '__rawAudio' ] = this[ '_rawObject' ]; + //window[ '__rawAudio' ] = this[ '_rawObject' ]; /* X_Audio_HTMLAudioWrapper_ieMobile9Fix && */ e.type !== 'timeupdate' && console.log( e.type ); @@ -184,8 +210,8 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ break; case 'canplay' : // 今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生 - if( X_Audio_HTMLAudioWrapper_durationFix && this._playForDuration === 0 ){ - //console.log( 'DurationFix開始 - ' + this[ '_rawObject' ].duration ); + if( X_Audio_HTMLAudioWrapper_durationFix && this._playForDuration === 0 && !X_UA[ 'AndroidChromeBrowser' ] ){ + console.log( '[duration fix]開始 - ' + this[ '_rawObject' ].duration ); this._playForDuration = 1; this[ '_rawObject' ].play(); this[ '_rawObject' ].currentTime = this._beginTime / 1000; // 必要! @@ -194,6 +220,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ case 'loadedmetadata' : // ブラウザがメディアリソースの長さと寸法を判定した場合に発生 case 'loadeddata' : // コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生 case 'canplaythrough' : // 今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生 + X_Audio_HTMLAudioWrapper_durationFix && console.log( '[duration fix]' + e.type + ' ' + this._playForDuration ); if( X_Audio_HTMLAudioWrapper_durationFix && this._playForDuration !== 2 ) return; this.duration = this.duration || this[ '_rawObject' ].duration * 1000; break; @@ -235,8 +262,9 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){ this.looped = true; this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); - this.actualPlay(); + this.actualPlay( X_Audio_HTMLAudioWrapper_needPlayOnended ); }; + // Android4.1.1 ended 後に曲の再生が継続できない return; }; type = X_EVENT_MEDIA_ENDED; @@ -249,11 +277,10 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ if( X_Audio_HTMLAudioWrapper_ieMobile9Fix ){ if( this._playForDuration === 1 ){ console.log( 'tu ' + this[ '_rawObject' ].duration ); - if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){ + if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) && 0 < this[ '_rawObject' ].duration ){ this.duration = this.duration || this[ '_rawObject' ].duration * 1000; this._playForDuration = 2; loaded = true; - //console.log( 'durationFix が完了' + this.duration ); break; } else { this[ '_rawObject' ].currentTime = this._beginTime / 1000; // 必要! @@ -261,7 +288,6 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ }; } else if( this[ '_rawObject' ].currentTime === this._lastCurrentTime ){ - //this.target[ 'dispatch' ]( 'seeking' ); this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); return; }; @@ -270,10 +296,10 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ this.duration = this.duration || this[ '_rawObject' ].duration * 1000; if( this.playing ){ - end = X_AudioWrapper_getEndTime( this ); + end = X_AudioWrapper_getEndTime( this ) + this.shortPlayFix; now = this.getActualCurrentTime(); console.log( now + ' / ' + end ); - if( 0 + end <= 0 + now ){ // なぜか iem9 で必要,,, + if( 0 + end <= 0 + now ){ // 0+ なぜか iem9 で必要,,, if( this.autoLoop ){ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){ this.looped = true; @@ -296,9 +322,10 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ if( !X_Audio_HTMLAudioWrapper_durationFix ){ this.duration = this[ '_rawObject' ].duration * 1000; + console.log( 'duration : ' + this.duration ); } else - // Desktop Opera では Infinity, IEM9 では NaN - if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){ + // Desktop Opera では Infinity, IEM9 では NaN, Android標準ブラウザ(Chrome)では 0 + if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) && 0 < this[ '_rawObject' ].duration ){ //console.log( this[ '_rawObject' ].duration ); @@ -309,20 +336,21 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ if( this._playForDuration === 1 ){ this._playForDuration = 2; - console.log( 'Loaded ' + this._loaded ); + console.log( '[duration fix] Loaded ' + this._loaded ); if( this._loaded ){ this[ '_rawObject' ].currentTime = this._beginTime / 1000; - console.log( '設定 ' + this._beginTime ); + console.log( '[duration fix] 設定 ' + this._beginTime ); return; }; loaded = true; - console.log( 'durationFix が完了' + this.duration ); + console.log( '[duration fix] 完了' + this.duration ); if( this.autoplay ){ this[ '_rawObject' ].currentTime = this._beginTime / 1000; - } else { + } else + if( X_UA[ 'Opera' ] ){ // Opera12.17 WinXP で勝手に再生される不具合 // これで一応再生は止まる、、、 this[ '_rawObject' ].src = ''; @@ -340,17 +368,18 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ this.autoplay && X_Timer_once( 16, this, this.play ); this._loaded = true; this.target[ 'asyncDispatch' ]( X_EVENT_READY ); - console.log( 'Loaded! ' + e.type + ' d:' + ( this.duration | 0 ) ); + console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) ); return; }; if( !loaded && type ){ + console.log( '(2) ' + e.type + ' d:' + ( this.duration | 0 ) ); this.target[ 'dispatch' ]( type ); type === X_EVENT_ERROR && this[ 'kill' ](); }; }, - actualPlay : function(){ + actualPlay : function( forcePlay ){ var begin, end; // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気 @@ -368,9 +397,11 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ end = X_AudioWrapper_getEndTime( this ); begin = this._beginTime = X_AudioWrapper_getStartTime( this, end, true ); - if( X_Audio_HTMLAudioWrapper_shortPlayFix ){ - begin -= ( end - begin > 1000 ) ? 200 : 400; - begin = begin < 0 ? 0 : begin; + if( X_Audio_HTMLAudioWrapper_shortPlayFix && this.isM4A ){ + this.shortPlayFix = ( 1000 < end - begin ) ? 200 : 400; + if( this.duration < end + this.shortPlayFix ){ + this.shortPlayFix = this.duration - end; + }; }; if( !this[ '_rawObject' ].src ){ @@ -379,7 +410,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ delete this._playForDuration; }; - if( !this.playing ){ + if( !this.playing || forcePlay ){ if( X_UA[ 'Chrome' ] ){ // [CHROME][FIX] volume TODO どの version で 修正される? // [!] delay X_Timer_once( 0, this, this._fixForChrome ); @@ -423,7 +454,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ !this[ '_rawObject' ].error && this[ '_rawObject' ].pause(); - if( X_Audio_HTMLAudioWrapper_durationFix ){ + if( X_Audio_HTMLAudioWrapper_durationFix && X_UA[ 'Opera' ] ){ this[ '_rawObject' ].src = ''; // load(); }; @@ -492,41 +523,6 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){ * - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。 */ detect : function( proxy, source, ext ){ - /* - var ok, mineType = 'audio/' + ext; - switch( ext ){ - case 'mp3' : - ok = X_UA[ 'IE' ] || X_UA[ 'Chrome' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ); - mineType = 'audio/mpeg'; - //if( X_UA[ 'Android' ] && X_UA[ 'Gecko' ] ) mineType = ''; - break; - case 'ogg' : - ok = 15 <= X_UA[ 'Gecko' ] || X_UA[ 'Chrome' ] || X_UA[ 'Opera' ] ; - if( X_UA[ 'AndroidBrowser' ] ) mineType = ''; - break; - case 'm4a' : - ok = X_UA[ 'IE' ] || X_UA[ 'WebKit' ]; - mineType = 'audio/mp4'; - break; - case 'webm' : - ok = 2 <= X_UA[ 'Gecko' ] || 10.6 <= X_UA[ 'Opera' ] ; // firefox4+(Gecko2+) - break; - case 'wav' : - ok = X_UA[ 'Gecko' ] || X_UA[ 'Opera' ] || ( X_UA[ 'Windows' ] && X_UA[ 'Safari' ] ); - //mineType = 'audio/wav'; // audio/x-wav ? - break; - default : - mineType = ''; - }; - - if( !ok && mineType ){ - if( !X_Audio_rawAudio ) X_Audio_rawAudio = new Audio; - ok = X_Audio_rawAudio.canPlayType( mineType ); - //console.log( 'HTML Audio ' + ok + ' ext:' + ext ); - }; - console.log( 'HTML Audio ' + ok + ' ext:' + ext ); - */ - proxy[ 'asyncDispatch' ]( { type : X_EVENT_COMPLETE, canPlay : X_Audio_codecs[ ext ] } ); }, diff --git a/0.6.x/js/07_audio/10_XAudioSprite.js b/0.6.x/js/07_audio/10_XAudioSprite.js index ac5b19e..53f5ed0 100644 --- a/0.6.x/js/07_audio/10_XAudioSprite.js +++ b/0.6.x/js/07_audio/10_XAudioSprite.js @@ -5,12 +5,17 @@ * iframe 内で生成して、Audio Sprite の preset で再生できないか? */ var X_Audio_Sprite_shouldUse = window.HTMLAudioElement && ( X_UA[ 'iOS' ] || X_UA[ 'AndroidBrowser' ] || X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ), // Flash がない - X_Audio_Sprite_useVideoForMulti = 4 <= X_UA[ 'AndroidBrowser' ] && 534.3 < X_UA[ 'AndroidWebkit' ], // ドスパラパッドはビデオのインライン再生が不可 - X_Audio_Sprite_needTouchAndroid = X_Audio_Sprite_useVideoForMulti, + X_Audio_Sprite_useVideoForMulti = //( X_UA[ 'AndroidBrowser3' ] && 3.1 <= X_UA[ 'AndroidBrowser' ] ) || + //( ( 4.2 <= X_UA[ 'AndroidBrowser' ] || ( 4.1 <= X_UA[ 'AndroidBrowser' ] && 2 <= X_UA[ 'AndroidPatch' ] ) ) && X_UA[ 'AndroidWebkit' ] <= 534.3 ), + // ドスパラパッドはビデオのインライン再生が不可, 534.30 で Webkit系は終了, 次は 537.36 で Chrome系 + false, //X_UA[ 'AndroidChromeBrowser' ], + X_Audio_Sprite_needTouchAndroid = X_UA[ 'AndroidChromeBrowser' ] && !X_Audio_WebAudioWrapper, X_Audio_Sprite_needTouchFirst = X_UA[ 'iOS' ] || X_Audio_Sprite_needTouchAndroid || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), - X_Audio_Sprite_enableMultiTrack = !( X_UA[ 'iOS' ] && !X_Audio_WebAudio_context ) && !( X_UA[ 'AndroidBrowser4' ] && X_UA[ 'AndroidWebkit' ] <= 534.3 ) && !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), + X_Audio_Sprite_disableMultiTrack = ( X_UA[ 'iOS' ] && !X_Audio_WebAudio_context ) || ( !X_UA[ 'AndroidChromeBrowser' ] && X_UA[ 'AndroidBrowser4' ] ) || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), X_Audio_Sprite_enableVolume = window.HTMLAudioElement && ( !X_UA[ 'iOS' ] && !X_UA[ 'AndroidBrowser' ] && !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] ), // TODO fennec は 25以上 - X_Audio_Sprite_maxTracks = !X_Audio_Sprite_enableMultiTrack ? 1 : X_Audio_Sprite_useVideoForMulti ? 2 : 9, + // http://tukumemo.com/html5-audio-sp/ + // iOS6、Android4.1から同時再生が可能になりました。 + X_Audio_Sprite_maxTracks = X_Audio_Sprite_useVideoForMulti ? 2 : X_Audio_Sprite_disableMultiTrack ? 1 : 9, X_Audio_Sprite_lengthSilence = 10000, // 一番最初の無音部分の長さ X_Audio_Sprite_lengthDistance = 5000, // 音間の無音の長さ X_Audio_Sprite_uid = 0, @@ -54,6 +59,12 @@ X[ 'AudioSprite' ] = function( setting ){ n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks; + // TODO + // Android4.x標準ブラウザ(Chrome系)でブラウザが隠れた場合に音が鳴り続ける問題、ビデオで解決できる? + //if( X_Audio_Sprite_needTouchAndroid && n === 1 ){ + // video = true; + //}; + for( k in setting ){ v = setting[ k ]; if( X_Type_isArray( v ) && v !== urls ){ @@ -80,7 +91,7 @@ X[ 'AudioSprite' ] = function( setting ){ X[ 'AudioSprite' ][ 'shouldUse' ] = X_Audio_Sprite_shouldUse; X[ 'AudioSprite' ][ 'needTouchFirst' ] = X_Audio_Sprite_needTouchFirst; -X[ 'AudioSprite' ][ 'enableMultiTrack' ] = X_Audio_Sprite_enableMultiTrack; +X[ 'AudioSprite' ][ 'enableMultiTrack' ] = !X_Audio_Sprite_disableMultiTrack; // 再生が終わっているもの、終わりかけのものを探す // TODO 終わりかけのもの、と一番古いもの、どちらを再利用するか?これ以上に細かい実装を望む場合は X.Audio.Sprite は使わず自力で実装 @@ -130,7 +141,13 @@ X_Audio_Sprite_members = { 'load' : function(){ var tracks = X_Audio_Sprite_TEMP.tracks, i = 0, l = tracks.length; + for( ; i < l; ++i ){ + if( X_Audio_Sprite_needTouchAndroid ){ + console.log( '[duration fix]開始 - ' + tracks[ i ][ '_rawObject' ].duration ); + tracks[ i ]._playForDuration = 1; + tracks[ i ][ '_rawObject' ].play(); + } else if( X_UA[ 'WinPhone' ] ){ console.log( 'WinPhone : touch -> play()' ); //tracks[ i ].play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 ); @@ -142,7 +159,7 @@ X_Audio_Sprite_members = { }, /* - * @return uid Number + * @return {number} uid */ 'play' : function( name ){ var bgm = X_Audio_Sprite_TEMP.bgmTrack, @@ -309,6 +326,7 @@ function X_AudioSprite_backendHandler( e ){ for( i = 0; i < X_Audio_Sprite_numTracks; ++i ){ if( X_Audio_Sprite_useVideo || ( i === 1 && X_Audio_Sprite_useVideoForMulti ) ){ option[ 'useVideo' ] = true; + console.log( 'use video' ); }; // Audiobackend の owner として null を渡すとAudioBackend 自身へ dispatch する X_Audio_Sprite_TEMP.tracks.push( last = backend.klass( null, e[ 'source' ], option ) ); @@ -338,7 +356,7 @@ function X_AudioSprite_backendHandler( e ){ last[ 'listenOnce' ]( X_EVENT_READY, this, X_AudioSprite_backendHandler ); // READY, needTouchForPlay, needTouchForLoad - if( X_Audio_HTMLAudioWrapper_durationFix ){ + if( X_Audio_HTMLAudioWrapper_durationFix && !X_Audio_Sprite_needTouchFirst ){ for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){ this[ 'pause' ]( i ); }; @@ -352,15 +370,15 @@ function X_AudioSprite_backendHandler( e ){ return X_CALLBACK_STOP_NOW; case X_EVENT_READY : - console.log( 'X.AudioSprite - Ready!' ); - if( X_Audio_Sprite_needTouchAndroid ){ for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){ this[ 'pause' ]( i ); }; - e.target[ 'listenOnce' ]( X_EVENT_MEDIA_PLAYING, this, this[ 'asyncDispatch' ], [ X_EVENT_READY ] ); // Android 標準ブラウザ + e.target[ 'listenOnce' ]( X_EVENT_MEDIA_PLAYING, this, this[ 'asyncDispatch' ], [ X_EVENT_READY ] ); return; }; + + console.log( 'X.AudioSprite - Ready!' ); this[ 'asyncDispatch' ]( X_EVENT_READY ); break; }; diff --git a/0.6.x/js/11_hid/01_KB.js b/0.6.x/js/11_hid/01_KB.js new file mode 100644 index 0000000..584cf0a --- /dev/null +++ b/0.6.x/js/11_hid/01_KB.js @@ -0,0 +1,318 @@ +/* + * JavaScript : Opera と Firefox でのキーイベントの違い + * http://blog.livedoor.jp/tzifa/archives/50776590.html + * keydown について。Firefox では押している間中リスナの関数が実行される + * デフォルトイベントの制御・抑止 (opera)keypress を用いる。 + * + * キーイベント処理の工夫 + * http://www.keynavi.net/ja/tipsj/kfunc.html + * keydown/up時にピリオドが文字化け (IE4-6) IE4+では「keypress」でキーコードを処理する 但しCtrlやALTが押されている場合は逆にkeydownで処理する必要があります + * + * + * keydown をトリガーにイベントを発火するもの + * 1. テンキーの 0~9 keyCode:96-105 + * 2. ScrollLock:145, Ins:45, PuaseBreak:19, HOME:36, PageUp:33, del:46, END:35, PageDown:34, + * ←↑→↓:37-40, tab:9, capsLock:240or208, Shift+capslock:20, shift:16, ctrl:17, LWIN:91, BS:8, + *     + * 3. RWIN:92!(Opera<9.5), alt:18, [F1]-[F12]:112-123 + * + */ + +var X_KB_TABLE = { + specials : { + '96' : 48, + '97' : 49, + '98' : 50, + '99' : 51, + '100' : 52, + '101' : 53, + '102' : 54, + '103' : 55, + '104' : 56, + '105' : 57, + + '8' : 'BS', + '9' : 'TAB', + '13' : 'ENTER', + '16' : 'SHIFT', + '17' : 'CTRL', + '18' : 'ALT', + '19' : 'PAUSE_BREAK', + '20' : 'SHIFT+CAPS_LOCK', + + '27' : 'ESC', + '28' : 'trans', + '29' : 'no trans', + + '33' : 'PAGE_UP', + '34' : 'PAGE_DOWN', + '35' : 'END', + '36' : 'HOME', + '37' : 'CSR_L', + '38' : 'CSR_U', + '39' : 'CSR_R', + '40' : 'CSR_D', + '45' : 'INS', + '44' : 'PRT_SCRN', + '46' : 'DEL', + + '91' : 'LWIN', + '92' : 'RWIN', + '93' : 'APP', + + '106' : 42, + '107' : 43, + '109' : 45, + '111' : 47, + '112' : 'F1', + '113' : 'F2', + '114' : 'F3', + '115' : 'F4', + '116' : 'F5', + '117' : 'F6', + '118' : 'F7', + '119' : 'F8', + '120' : 'F9', + '121' : 'F10', + '122' : 'F11', + '123' : 'F12', + + '144' : 'NUM_LOCK', + '145' : 'SCROLL_LOCK', + '208' : 'CAPS_LOCK', + '240' : 'CAPS_LOCK', + '242' : 'K/H', + '243' : 'H/Z', + '244' : 'H/Z' + + }, + // keypress 時に keyCode を直す 0 の場合、イベント発火せず。どのキーが押されたか?判定できないため + 'keypress' : { + // !-):33-41, *:42, +:43, ,:44, -:45, .:46, /:47, 0-9:48-57 , ::58 , ;:59 , <:60 , =:61, >:62, ?:63, @:64 + // A-Z:65-90, [:91, \:92, ]:93, ^:94, _:95, `:96, a-z:97-122, {:123, |:124, }:125, ~:126 + }, + + 'keyup' : { + '189' : '45,61', // ie, safari + '109' : '45,61', // firefox, opera + + '222' : '94,126', // firefox, ie, safari + '94' : '94,126', // opera + + '226' : '92,95', // firefox, ie, safari, + '220' : 9.5 <= X_UA[ 'Opera' ] ? '92,95,124' : '92,124', // firefox, ie, safari, opera9.50+ + '92' : '92,95,124', // opera9.25 + + '192' : '64,96', // firefox, ie, safari + '64' : '64,96', // opera + + '219' : '91,123', // firefox, ie, safari, opera9.50+ + '91' : '91,123', // opera9.25 + + '187' : '59,43', // ie, safari + '61' : '59,43', // firefox, opera + + '186' : '58,42', // ie, safari + '59' : '58,42', // firefox, opera + + '221' : '93,125', // firefox, ie, safari, opera9.50+ + '93' : '93,125', // opera9.25 + + '188' : '44,60', // firefox, ie, safari, opera9.50+ + '44' : '44,60', // opera9.25 + + '190' : '46,62', // firefox, ie, safari, opera9.50+ + '46' : '46,62', // opera9.25 + + '191' : '47,63', // firefox, ie, safari, opera9.50+ + '47' : '47,63' // opera9.25 + } + }, + X_KB_DOWN_KEYS = {}, + X_KB_CANCELED = {}, + X_KB_lastIs10Key = 0, + X_KB_lastKeyCode = 0, + X_KB_TRANSFOEM = {}, + + X_kbManager = + X_Class_override( + X_EventDispatcher(), + { + handleEvent : function( e ){ + var keyCode = e.keyCode, // keyCode says something about the actual keyboard key the user pressed + chrCode = e.charCode, // while charCode gives the ASCII value of the resulting character + cb = X_CALLBACK_NONE, + special, is10Key, _keyCode; + + console.log( e.type + ' > keyCode:' + keyCode + ' chrCode:' + chrCode ); + + switch( e.type ){ + case 'keydown' : + + if( X_KB_DOWN_KEYS[ keyCode ] ){ + // 既に押されている、メタキー[shift,ctrl,alt]の変化はある? + return cb; + } else + if( special = X_KB_TABLE.specials[ keyCode ] ){ + + if( X_Type_isNumber( special ) ){ + // テンキーの [0]~[9] + //chrCode = special; + X_KB_lastKeyCode = keyCode; + X_KB_lastIs10Key = true; + return cb; + } else { + X_KB_DOWN_KEYS[ keyCode ] = true; + chrCode = 0; + }; + + cb = this[ 'dispatch' ]( { + type : 'keydown', + keyCode : keyCode, + charCode : chrCode, + keyName : X_Type_isString( special ) ? special : '', + is10key : !!is10Key, + shiftKey : !!X_KB_DOWN_KEYS[ 16 ], + ctrlKey : !!X_KB_DOWN_KEYS[ 17 ], + altKey : !!X_KB_DOWN_KEYS[ 18 ], + metaKey : !!X_KB_DOWN_KEYS[ 224 ] + } ); + + if( cb & X_CALLBACK_PREVENT_DEFAULT ){ + X_KB_CANCELED[ keyCode ] = true; + }; + } else { + X_KB_lastKeyCode = keyCode; + }; + + return cb; + + case 'keypress' : + // keydown 側で発火しているものは再び発火しない + console.log( 'kp : ' + X_KB_DOWN_KEYS[ chrCode ] + ( X_KB_CANCELED[ chrCode ] ? ' Cancel!' : '' ) ); + if( X_KB_DOWN_KEYS[ chrCode ] ){ + return X_KB_CANCELED[ chrCode ] ? X_CALLBACK_PREVENT_DEFAULT : cb; + }; + + if( 33 <= chrCode && chrCode <= 126 ){ + X_KB_TRANSFOEM[ X_KB_lastKeyCode ] = chrCode; + + cb = this[ 'dispatch' ]( { + type : 'keydown', + keyCode : X_KB_lastIs10Key ? X_KB_lastKeyCode : 0, + charCode : chrCode, + is10key : X_KB_lastIs10Key, + shiftKey : !!X_KB_DOWN_KEYS[ 16 ], + ctrlKey : !!X_KB_DOWN_KEYS[ 17 ], + altKey : !!X_KB_DOWN_KEYS[ 18 ], + metaKey : !!X_KB_DOWN_KEYS[ 224 ] + } ); + + X_KB_lastIs10Key = true; + }; + return cb; + + case 'keyup' : + if( X_KB_CANCELED[ keyCode ] ){ + cb = X_CALLBACK_PREVENT_DEFAULT; + }; + + if( ( special = X_KB_TABLE.specials[ keyCode ] ) && ( !X_KB_DOWN_KEYS[ keyCode ] && !X_KB_DOWN_KEYS[ special ] ) ){ + cb |= this[ 'dispatch' ]( { + type : 'keydown', + keyCode : keyCode, + charCode : 0, + keyName : special, + is10key : false, + isVirtual : true, + shiftKey : !!X_KB_DOWN_KEYS[ 16 ], + ctrlKey : !!X_KB_DOWN_KEYS[ 17 ], + altKey : !!X_KB_DOWN_KEYS[ 18 ], + metaKey : !!X_KB_DOWN_KEYS[ 224 ] + } ); + }; + + if( X_KB_DOWN_KEYS[ keyCode ] ) delete X_KB_DOWN_KEYS[ keyCode ]; + if( X_KB_CANCELED[ keyCode ] ) delete X_KB_CANCELED[ keyCode ]; + + chrCode = 0; + if( !special ){ + chrCode = X_KB_TRANSFOEM[ keyCode ]; + if( !chrCode ) return cb; + delete X_KB_TRANSFOEM[ keyCode ]; + //keyCode = 0; + }; + + cb |= this[ 'dispatch' ]( { + type : 'keyup', + keyCode : keyCode, + charCode : chrCode, + keyName : X_Type_isString( special ) ? special : '', + shiftKey : X_KB_DOWN_KEYS[ 16 ], + ctrlKey : X_KB_DOWN_KEYS[ 17 ], + altKey : X_KB_DOWN_KEYS[ 18 ], + metaKey : X_KB_DOWN_KEYS[ 224 ] + } ); + + return cb; + + case X_EVENT_VIEW_ACTIVATE : + // + break; + case X_EVENT_VIEW_DEACTIVATE : + // + break; + }; + } + } + ), + +/** + * キーボードイベント情報を提供するオブジェクト。 + * @namespace X.KB + * @alias X.KB + */ + X_KB = X[ 'KB' ] = { + /** + * + * @alias X.KB.listen + */ + 'listen' : function( type, arg1, arg2, arg3 ){ + type && arg1 && X_kbManager[ 'listen' ]( type, arg1, arg2, arg3 ); + return X_KB; + }, + + /** + * + * @alias X.KB.listenOnce + */ + 'listenOnce' : function( type, arg1, arg2, arg3 ){ + type && arg1 && X_kbManager[ 'listenOnce' ]( type, arg1, arg2, arg3 ); + return X_KB; + }, + + /** + * + * @alias X.KB.unlisten + */ + 'unlisten' : function( type, arg1, arg2, arg3 ){ + type && arg1 && X_kbManager[ 'unlisten' ]( type, arg1, arg2, arg3 ); + return X_KB; + }, + + /** + * + * @alias X.KB.listening + */ + 'listening' : function( type, arg1, arg2, arg3 ){ + return X_kbManager[ 'listening' ]( type, arg1, arg2, arg3 ); + } + }; + +X_ViewPort[ 'listen' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_kbManager ); + +if( X_UA[ 'IE' ] < 9 ){ + X_ViewPort_document[ 'listen' ]( [ 'keyup', 'keydown', 'keypress' ], X_kbManager ); +} else { + X_ViewPort[ 'listen' ]( [ 'keyup', 'keydown', 'keypress' ], X_kbManager ); +}; diff --git a/0.6.x/js/import.js b/0.6.x/js/import.js index 7d9dd92..8063af2 100644 --- a/0.6.x/js/import.js +++ b/0.6.x/js/import.js @@ -64,7 +64,9 @@ document.write( [ 'js/07_audio/01_XWebAudio.js', 'js/07_audio/02_XHTMLAudio.js', 'js/07_audio/03_XSilverlightAudio.js', - 'js/07_audio/10_XAudioSprite.js' + 'js/07_audio/10_XAudioSprite.js', + + 'js/11_hid/01_KB.js' + '">' ].join( '">