OSDN Git Service

Version 0.6.149, fix X.Audio & X.UI.
authoritozyun <itozyun@user.sourceforge.jp>
Tue, 19 May 2015 10:48:07 +0000 (19:48 +0900)
committeritozyun <itozyun@user.sourceforge.jp>
Tue, 19 May 2015 10:48:07 +0000 (19:48 +0900)
33 files changed:
0.6.x/js/01_core/03_XType.js
0.6.x/js/01_core/09_XPair.js
0.6.x/js/01_core/11_XClass.js
0.6.x/js/01_core/12_XEvent.js
0.6.x/js/01_core/13_XEventDispatcher.js
0.6.x/js/01_core/16_XViewPort.js
0.6.x/js/02_dom/00_XDoc.js
0.6.x/js/02_dom/05_XNodeAttr.js
0.6.x/js/02_dom/06_XNodeCSS.js
0.6.x/js/02_dom/08_XNodeSelector.js
0.6.x/js/02_dom/10_XNodeAnime.js
0.6.x/js/03_plugin/00_XPlugin.js
0.6.x/js/05_util/04_XXML.js [new file with mode: 0644]
0.6.x/js/06_net/00_XNet.js
0.6.x/js/06_net/01_XNetXHR.js
0.6.x/js/06_net/05_XXHRGadget.js [new file with mode: 0644]
0.6.x/js/06_net/10_XOAuth2.js [new file with mode: 0644]
0.6.x/js/07_audio/00_XAudio.js
0.6.x/js/07_audio/01_XWebAudio.js
0.6.x/js/07_audio/02_XHTMLAudio.js
0.6.x/js/07_audio/03_XSilverlightAudio.js
0.6.x/js/07_audio/10_XAudioSprite.js
0.6.x/js/20_ui/00_XUI.js
0.6.x/js/20_ui/02_XUI_Attr.js
0.6.x/js/20_ui/06_AbstractUINode.js
0.6.x/js/20_ui/08_Box.js
0.6.x/js/20_ui/11_VBox.js
0.6.x/js/20_ui/12_HBox.js
0.6.x/js/20_ui/13_TileBox.js
0.6.x/js/20_ui/14_ChromeBox.js
0.6.x/js/20_ui/15_ScrollBox.js
0.6.x/js/20_ui/17_Text.js
0.6.x/js/20_ui/20_PageRoot.js

index a7a78aa..ed024ae 100644 (file)
@@ -1,10 +1,9 @@
-var\r
        /**\r
         * Array か?判定する。argumnets 等のフェイク Array は false なので注意。\r
-        * @funciton\r
-        * @alias X.Type._isArray\r
+        * @function\r
+        * @alias X.Type.isArray\r
         */\r
-       X_Type_isArray =\r
+var X_Type_isArray =\r
                new Function( 'v',\r
                        X_UA[ 'IE' ] < 5.5 || X_UA[ 'NetFront' ] < 4 ? // netfront3.4 は html に  instanceof をすると error になる\r
                                'return v&&v.push===Array.prototype.push' : // win ie5-, MacIE5.2\r
@@ -15,13 +14,13 @@ var
 \r
        /**\r
         * HTMLElement か?判定する。ちなみに return v instanceof Element は ie8 でエラー。\r
-        * @funciton\r
+        * @function\r
         * @alias X.Type.isHTMLElement\r
         */\r
        X_Type_isHTMLElement =\r
                new Function( 'v',\r
                        ( X_UA[ 'IE4' ] || X_UA[ 'MacIE' ] ) ?\r
-                               'return v&&v.tagName&&v.insertAdjacentHTML&&true' : // ie4 or MacIE5.23, v.all <- error\r
+                               'return v&&v.tagName&&v.insertAdjacentHTML&&!0' : // ie4 or MacIE5.23, v.all <- error\r
                        X_UA[ 'NetFront' ] < 4 ?\r
                                'return v&&v.nodeType===1' : // instanceof not a function. netfront3.4 は html に  instanceof をすると error になる\r
                        window[ 'HTMLElement' ] ?\r
@@ -32,9 +31,8 @@ var
 \r
 \r
 /**\r
- * http://pettanr.sourceforge.jp/test/type.html\r
- * ビルトイン方の判定に使用する関数を集めたもの。ブラウザのネイティブな判定関数には不可解な挙動があるので、X.Type を使用するほうがよい。\r
- * \r
+ * <p>ビルトイン型の判定に使用する関数を集めたもの。ブラウザのネイティブな判定関数には不可解な挙動があるので、X.Type を使用するほうがよい。\r
+ * <a href="http://pettanr.sourceforge.jp/test/type.html">http://pettanr.sourceforge.jp/test/type.html</a>\r
  * @namespace X.Type\r
  * @alias X.Type\r
  */\r
@@ -55,12 +53,13 @@ X[ 'Type' ] = {
 };\r
 \r
        /**\r
-        * Object か?判定する。typeof null === 'object' に対策済なので null は Object ではない。\r
-        * new String(), new Number(), new Boolean() も typeof object なので対策\r
+        * <p>Object か?判定する。\r
+        * <p>typeof null === 'object' に対策済なので null は Object ではない。\r
+        * <p>new String(), new Number(), new Boolean() も typeof object なので対策\r
         * @alias X.Type.isObject\r
         */\r
        function X_Type_isObject( v ){\r
-               return v && typeof v === 'object'; // && ( v !== v + '' && v !== v + 0 && v !== true ) ; // typeof null === 'object' に対策\r
+               return v && typeof v === 'object' && v !== v + '' && v !== v + 0 && v !== true; // typeof null === 'object' に対策\r
        };\r
        /**\r
         * Function か?判定する。\r
@@ -89,14 +88,14 @@ X[ 'Type' ] = {
         * @alias X.Type.isString\r
         */\r
        function X_Type_isString( v ){\r
-               return typeof v === 'string'; // v === v + ''; // 文字列の加算は IE で遅いかも。\r
+               return /* typeof v === 'string'; */ v === v + ''; // 文字列の加算は IE で遅いかも。\r
        };\r
        /**\r
-        * 数値か?判定する。\r
+        * 数値か?判定する。\r
         * @alias X.Type.isNumber\r
         */\r
        function X_Type_isNumber( v ){\r
-               return typeof v === 'number'; // v !== v || v + 0 === v;\r
+               return /* typeof v === 'number'; */ v + 0 === v || v !== v;\r
        };\r
        /**\r
         * finite か?判定する。isFinite( '123' ) === true に対策済。\r
@@ -120,7 +119,7 @@ X[ 'Type' ] = {
         */     \r
        function X_Type_isImage( v ){\r
                if( v && v.constructor === window.Image ) return true;\r
-               if( v && window.HTMLImageElement && v.constructor === window.HTMLImageElement ) return true; // ie6- は constructor が undef、HTMLImageElement が undef なので、HTMLElement の存在確認が必要\r
+               if( v && window.HTMLImageElement && v.constructor === window.HTMLImageElement ) return true; // ie6- は constructor が undef、HTMLImageElement が undef なので、HTMLElement の存在確認が必要\r
                if( X_UA[ 'WebKit' ] < 525.13 ){ // Safari3-\r
                        if( v && v.src !== undefined && v.onload !== undefined && X_Type_isNumber( v.height ) && X_Type_isNumber( v.width ) && X_Type_isBoolean( v.complete ) ){\r
                                return true;\r
@@ -145,7 +144,7 @@ X[ 'Type' ] = {
         * @alias X.Type.isUndefined\r
         */\r
        function X_Type_isUndefined( v ){\r
-               return v === void 0;\r
+               return v === undefined;\r
        };\r
 \r
 console.log( 'X.Core.Type' );\r
index 60fb3dc..120258c 100644 (file)
@@ -31,7 +31,7 @@ function X_Pair_create( key, pair ){
                pairStore = X_Pair_PAIR_STORE_LIST[ X_Pair_PAIR_STORE_LIST.length - 1 ];\r
        \r
        X_Pair_noChashe = true;\r
-       if( X_Pair_get( key ) ) return;\r
+       if( X_Pair_get( key ) || !( X_Type_isObject( key ) || X_Type_isArray( key ) || X_Type_isFunction( key ) ) ) return;\r
        \r
        if( keyStore.length === X_Pair_SIZE ){\r
                keyStore  = X_Pair_KEY_STORE_LIST[ X_Pair_KEY_STORE_LIST.length   ] = [];\r
index be37dc2..cbaeb22 100644 (file)
@@ -20,11 +20,8 @@ var
 \r
        X_Class_CLASS_LIST         = [],\r
        X_Class_DEF_LIST           = [],\r
-       X_Class_PRIVATE_CLASS_LIST = [],\r
-       X_Class_PRIVATE_DEF_LIST   = [],\r
        X_Class_CALLING_SUPER      = [],\r
        X_Class_CALL_SUPER_STACK   = [],\r
-       X_Class_killPrivateFlag    = false,\r
        X_Class_traits             = null,\r
        X_Class_useObjectCreate    = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf\r
        X_Class_use_proto_         = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && !!X_emptyFunction.prototype.__proto__,\r
@@ -48,26 +45,18 @@ X_Class_CommonMethods =
                        klass    = X_Class_getClass( instance ),\r
                        def      = X_Class_getClassDef( klass ),\r
                        data, p, i;\r
-               if( def.isPrivate && !X_Class_killPrivateFlag && ( !this[ '_listeners' ] || !this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] ) ){\r
-                       X.Logger.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );\r
-                       return;\r
-               };\r
-               X_Class_killPrivateFlag = false; // instance.kill() 内で PrivateInstance.kill() を防ぐため\r
                \r
                // TODO kill 中の kill の呼び出しを防ぐ, 破棄済のインスタンスへの kill\r
                \r
                if( this[ 'instanceOf' ]( X_EventDispatcher ) ){\r
-                       if( !def.isPrivate ){\r
-                               if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){\r
-                                       this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );\r
-                                       return;\r
-                               };\r
-                               if( this[ '_listeners' ] && this[ '_listeners' ][ X_Listeners_.DISPATCHING ] ){\r
-                                       this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;\r
-                                       return;\r
-                               };\r
-                       } else {\r
-                               this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE );     \r
+\r
+                       if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){\r
+                               this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );\r
+                               return;\r
+                       };\r
+                       if( this[ '_listeners' ] && this[ '_listeners' ][ X_LISTENERS_DISPATCHING ] ){\r
+                               this[ '_listeners' ][ X_LISTENERS_KILL_RESERVED ] = true;\r
+                               return;\r
                        };\r
 \r
                        // asyncDispatch の削除\r
@@ -90,20 +79,6 @@ X_Class_CommonMethods =
                        def.live && def.live.splice( def.live.indexOf( instance ), 1 );\r
                        def.pool[ def.pool.length ] = instance;\r
                };\r
-               if( def.privateClass ){\r
-                       i = def.userList.indexOf( instance );\r
-                       if( i !== -1 ){\r
-                               data = X_Class_getPrivate( instance );\r
-                               if( data[ '_listeners' ] && data[ '_listeners' ][ X_Listeners_.DISPATCHING ] && data[ 'instanceOf' ]( X.EventDispatcher ) ){\r
-                                       data[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;\r
-                               } else {\r
-                                       X_Class_killPrivateFlag = true;\r
-                                       data[ 'kill' ]();\r
-                               };\r
-                               def.dataList.splice( i, 1 );\r
-                               def.userList.splice( i, 1 );\r
-                       };\r
-               };\r
        },\r
        \r
        /**\r
@@ -234,9 +209,7 @@ var X_Class = {
        POOL_OBJECT  :  1,\r
        ABSTRACT     :  2,\r
        FINAL        :  4,\r
-       SUPER_ACCESS :  8,\r
-       PRIVATE_DATA : 16,\r
-       SINGLETON    : 32\r
+       SINGLETON    :  8\r
 };\r
 \r
 /**\r
@@ -250,7 +223,7 @@ var X_Class = {
  * </ol>\r
  * \r
  * <ol>\r
- * <li>X_Class.create( opt_settings, opt_name, opt_privateClass, opt_props ) でクラスを登録.\r
+ * <li>X.Class.create( opt_settings, opt_name, opt_props ) でクラスを登録.\r
  * <li>コンストラクタ となるメソッドは、opt_props 内の Constructor : function( arg ){ ... }, に書く.\r
  * <li>通常通り new で インスタンス生成\r
  * <li>kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.\r
@@ -259,7 +232,7 @@ var X_Class = {
  * @namespace X.Class\r
  * @alias X.Class\r
  */ \r
-X[ 'Class' ] = {\r
+X[ 'Class' ] = /** @lends X.Class */ {\r
 \r
     /**\r
      * 設定なし。\r
@@ -286,32 +259,14 @@ X[ 'Class' ] = {
        'FINAL'        :  X_Class.FINAL,\r
 \r
        /**\r
-        * 使用を中止。petanR ライブラリ使用プロジェクトから SUPER_ACCESS を消したらここも削除。\r
-        * @const\r
-        */\r
-       'SUPER_ACCESS' :  X_Class.SUPER_ACCESS,\r
-\r
-       /**\r
-        * 内部コード、主に X.UI フレームワークに対して、フレーム外に露出するインスタンスとペアで動作する、シャドウなインスタンスの使用を宣言する。\r
-        * Javascript はインスタンス毎のカプセル化がとてもコスト高。微妙なコスト増で隠蔽されたインスタンスを使う。\r
-        * @const\r
-        */\r
-       'PRIVATE_DATA' : X_Class.PRIVATE_DATA,\r
-\r
-       /**\r
         * 未実装。でも目印になるので付けておきましょう。\r
         * @const\r
         */\r
        'SINGLETON'    : X_Class.SINGLETON,\r
 \r
-       'create'       : X_Class_create,\r
+       'create'       : X_Class_create\r
        \r
        // TODO collect\r
-       \r
-       '_newPrivate'  : X_Class_newPrivate,\r
-       \r
-       '_getPrivate'  : X_Class_getPrivate\r
-       \r
 };\r
 \r
 \r
@@ -321,19 +276,34 @@ X[ 'Class' ] = {
 // ------------------------------------------------------------------------- //\r
        /**\r
         * クラスを定義する。<br>\r
-        * X_Class.create() によるクラス定義は必ずしもコンストラクタを必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。\r
-        * @alias X_Class.create\r
+        * X.Class.create() によるクラス定義は必ずしもコンストラクタ('Constructor')を必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。\r
+        * @alias X.Class.create\r
         * @param {string} [displayName] クラスの名前\r
         * @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など\r
-        * @param {__ClassBase__=} [privateClass] このクラスとペアで動作するシャドウクラス\r
         * @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと\r
         * @return {__ClassBase__}\r
+        * @example var myClass = X.Class.create(\r
+        *      'myClass',\r
+        *  X.Class.FINAL,\r
+        *  {\r
+        *       name : '',\r
+        *       Constructor : function( obj ){\r
+        *        this.name = obj.name;\r
+        *       },\r
+        *       getName : function(){\r
+        *        return this.name;\r
+        *       },\r
+        *       setName : function(v){\r
+        *        this.name = v;\r
+        *       }\r
+        *  }\r
+        * );\r
         */\r
        function X_Class_create( /* displayName, classSetting, privateClass, props */ ){\r
                var args        = X_Object_cloneArray( arguments ),\r
                        displayName = args[ 0 ],\r
                        classSetting,\r
-                       opt_pool, opt_abstract, opt_final, opt_private,\r
+                       opt_pool, opt_abstract, opt_final,\r
                        privateDef,\r
                        props,\r
                        klass,\r
@@ -349,7 +319,6 @@ X[ 'Class' ] = {
                        opt_pool     = !!( classSetting & X_Class.POOL_OBJECT  );\r
                        opt_abstract = !!( classSetting & X_Class.ABSTRACT     );\r
                        opt_final    = !!( classSetting & X_Class.FINAL        );\r
-                       opt_private  = !!( classSetting & X_Class.PRIVATE_DATA );\r
                        if( opt_final && opt_abstract ){\r
                                X.Logger.critical( 'final & Abstract!' );\r
                                return;\r
@@ -359,20 +328,6 @@ X[ 'Class' ] = {
                        classDef.setting = 0;\r
                };\r
                \r
-               // シャドウクラス\r
-               if( X_Class_PRIVATE_CLASS_LIST.indexOf( args[ 0 ] ) !== -1 ){\r
-                       privateDef = X_Class_getClassDef( args[ 0 ] );\r
-                       if( privateDef.isPrivate !== true ){\r
-                               X.Logger.critical( 'PrivateClass not found! please, X_Class.create( X_Class.PRIVATE, {...} ).' );\r
-                               return;\r
-                       } else\r
-                       if( privateDef.Abstract === true ){\r
-                               X.Logger.critical( 'PrivateClass is Abstract!' );\r
-                               return;\r
-                       };\r
-                       classDef.privateClass = args.shift();\r
-               };\r
-               \r
                // インスタンスのメンバー\r
                props = args[ 0 ];\r
                if( !X_Type_isObject( props ) ){\r
@@ -411,25 +366,17 @@ X[ 'Class' ] = {
                } else\r
                if( opt_pool ){\r
                        classDef.pool = [];\r
-                       if( opt_private === false ) classDef.live = [];\r
+                       classDef.live = [];\r
                };                      \r
                if( opt_final ){\r
                        classDef.Final = true;\r
                } else {\r
                        klass[ 'inherits' ] = X_Class_inherits;\r
                };                      \r
-               if( opt_private ){\r
-                       if( classDef.privateClass ){\r
-                               X.Logger.critical( 'Private Data Class has no PrivateClass!' );\r
-                               return;\r
-                       };\r
-                       classDef.isPrivate = true;\r
-                       X_Class_PRIVATE_CLASS_LIST.push( klass );\r
-                       X_Class_PRIVATE_DEF_LIST.push( classDef );\r
-               } else {\r
-                       X_Class_CLASS_LIST.push( klass );\r
-                       X_Class_DEF_LIST.push( classDef );                              \r
-               };\r
+               \r
+               X_Class_CLASS_LIST.push( klass );\r
+               X_Class_DEF_LIST.push( classDef );                              \r
+\r
                return klass;\r
        };\r
 \r
@@ -443,15 +390,7 @@ function X_Class_getClass( instance ){
                klass = cList[ --i ];\r
                if( instance.constructor === klass ) return klass;\r
        };\r
-       cList = X_Class_PRIVATE_CLASS_LIST;\r
-       i     = cList.length;\r
-       for( ; i; ){\r
-               klass = cList[ --i ];\r
-               if( instance.constructor === klass ) return klass;\r
-       };\r
-       \r
        if( cList.indexOf( instance ) !== -1 ) return instance;\r
-       if( X_Class_CLASS_LIST.indexOf( instance ) !== -1 ) return instance;\r
 };\r
 \r
 function X_Class_getClassDef( KlassOrInstance ){\r
@@ -459,43 +398,7 @@ function X_Class_getClassDef( KlassOrInstance ){
        if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
        if( i !== -1 ) return X_Class_DEF_LIST[ i ];\r
        \r
-       i = X_Class_PRIVATE_CLASS_LIST.indexOf( KlassOrInstance );\r
-       if( i === -1 ) i = X_Class_PRIVATE_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
-       if( i !== -1 ) return X_Class_PRIVATE_DEF_LIST[ i ];\r
-       \r
        if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
-       if( X_Class_PRIVATE_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
-};\r
-\r
-function X_Class_newPrivate( /* instance, args */ ){\r
-       var args         = X_Object_cloneArray( arguments ),\r
-               user         = args.shift(),\r
-               def          = X_Class_getClassDef( user ),\r
-               privateClass = def.privateClass,\r
-               privateDef   = X_Class_getClassDef( privateClass ),\r
-               i            = -1;\r
-       if( def.userList ){\r
-               i = def.userList.indexOf( user );\r
-       } else {\r
-               def.userList = [];\r
-               def.dataList = [];\r
-       };\r
-       if( i !== -1 ){\r
-               X.Logger.critical( 'PrivateData already exist!' );\r
-               return;\r
-       };\r
-       if( privateDef._tempUser ){\r
-               X.Logger.critical( 'newPrivate を連続呼び出しされたところ破綻' );\r
-               return;\r
-       };\r
-       privateDef._tempUser = user;\r
-       return X_Class_actualConstructor( privateClass( X_Closure_COMMAND_BACK ), args );// privateClass.__new( args );\r
-};\r
-\r
-function X_Class_getPrivate( instance ){\r
-       var def = X_Class_getClassDef( instance ),\r
-               i   = def.userList.indexOf( instance );\r
-       if( i !== -1 ) return def.dataList[ i ];\r
 };\r
 \r
 /* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */\r
@@ -582,16 +485,12 @@ function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props
                // クラス設定がない場合、親からコピーして、Abstract flag は落とす??\r
                classSetting = superDef.setting;// &= ~X_Class.ABSTRACT;\r
        };\r
-       if( superDef.isPrivate ) classSetting = classSetting | X_Class.PRIVATE_DATA;\r
 \r
        params.push( classSetting );\r
 \r
        // サブクラスのシャドウ\r
        if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){\r
                params.push( args.shift() );\r
-       } else\r
-       if( superDef.privateClass ){\r
-               params.push( superDef.privateClass );\r
        };\r
        \r
        /* props 未定義でも可 */\r
@@ -628,14 +527,11 @@ function X_Class_actualConstructor( f, args ){
                dataUser = def._tempUser,\r
                instance, obj,\r
                userDef;\r
+\r
        if( def.Abstract ){\r
                X.Logger.critical( 'AbstractClass!' );\r
                return;\r
        };\r
-       if( def.isPrivate && !dataUser ){\r
-               X.Logger.critical( 'use myClass.newPrivate( instance, ...args )!' );\r
-               return;\r
-       };\r
        \r
        instance = def.pool && def.pool.length > 0 ?\r
                                        def.pool.pop() :\r
@@ -643,21 +539,14 @@ function X_Class_actualConstructor( f, args ){
                                        Object.create( klass.prototype ) :\r
                                        new klass( X_Closure_COMMAND_DROP );\r
        \r
-       if( def.isPrivate ){\r
-               userDef = X_Class_getClassDef( dataUser );\r
-               userDef.dataList.push( instance );\r
-               userDef.userList.push( dataUser );\r
-               instance.User = dataUser;\r
-               def._tempUser = null;\r
-       } else {\r
-               def.live && def.live.push( instance );\r
-       };\r
+       def.live && def.live.push( instance );\r
 \r
        obj = def.Constructor ?\r
                        def.Constructor.apply( instance, args ) :\r
                def.SuperConstructor &&\r
                        def.SuperConstructor.apply( instance, args );\r
-       if( ( X_Type_isObject( obj ) && obj !== instance ) || X_Type_isFunction( obj ) ){ // Class\r
+\r
+       if( obj !== instance && ( X_Type_isObject( obj ) || X_Type_isFunction( obj ) ) ){ // Class\r
                instance[ 'kill' ]();\r
                return obj;\r
        };\r
index 5074db7..8b1c278 100644 (file)
@@ -50,7 +50,7 @@ var X_Event_Rename    = {},
                });\r
 \r
 var // 内部イベント\r
-       X_EVENT_PRE_INIT               =  5, // X_Listeners_.KILL_RESERVED に +1 した値から開始。\r
+       X_EVENT_PRE_INIT               =  5, // X_LISTENERS_KILL_RESERVED に +1 した値から開始。\r
        X_EVENT_XTREE_READY            =  6,\r
        X_EVENT_INIT                   =  7,\r
 \r
@@ -113,7 +113,9 @@ var // 内部イベント
        X_EVENT_MEDIA_WAITING          = 47,\r
        X_EVENT_MEDIA_SEEKING          = 48,\r
        \r
-       X_Event_last                   = 48;\r
+       X_EVENT_NEED_AUTH              = 49,\r
+       \r
+       X_Event_last                   = 49;\r
 \r
 /**\r
  * フレームワーク内で定義されたイベント。\r
@@ -223,7 +225,9 @@ X[ 'Event' ] = {
        'MEDIA_PAUSED'           : X_EVENT_MEDIA_PAUSED,\r
        'MEDIA_ENDED'            : X_EVENT_MEDIA_ENDED,\r
        'MEDIA_WAITING'          : X_EVENT_MEDIA_WAITING,\r
-       'MEDIA_SEEKING'          : X_EVENT_MEDIA_SEEKING\r
+       'MEDIA_SEEKING'          : X_EVENT_MEDIA_SEEKING,\r
+       \r
+       'NEED_AUTH'              : X_EVENT_NEED_AUTH\r
 };\r
 \r
 X_TEMP.onSystemReady.push(\r
index 612268f..f798c2a 100644 (file)
@@ -10,7 +10,7 @@
  * \r
  * <dl>\r
  * <dt>0:ACTUAL_HANDLER\r
- * <dd>ã\82¤ã\83\99ã\83³ã\83\88ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\81® addEventListener ç­\89ã\81«æ¸¡ã\81\95ã\82\8cã\82\8bå®\9fé\9a\9bã\81®é\96¢æ\95°(å\86\8då\88©ç\94¨å\8f¯è\83½ã\82¯ã\83­ã\83¼ã\82¸ã\83£)を控えています。\r
+ * <dd>ã\82¤ã\83\99ã\83³ã\83\88ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\81® addEventListener ç­\89ã\81«æ¸¡ã\81\95ã\82\8cã\82\8bå®\9fé\9a\9bã\81®é\96¢æ\95°(å¤\9aã\81\8fã\81®å ´å\90\88ã\80\81å\86\8då\88©ç\94¨å\8f¯è\83½ã\82¯ã\83­ã\83¼ã\82¸ã\83£ã\80\81ã\81\9dã\82\8c以å¤\96ã\81¯é\80\9a常ã\81®é\96¢æ\95°)を控えています。\r
  * <dt>1:DISPATCHING number\r
  * <dd>dispatch 中か?さらにインスタンス自身の dispatch がネストした場合、その深さを記憶します。\r
  * <dt>2:RESERVES Array\r
  * <dd>dispatch 中に kill() が呼ばれた場合に一旦 kill をキャンセルし、完了時(DISPATCHING===0)に再度 kill() するためのフラグです。\r
  * </dl>\r
  * \r
- * @class __X_EventDispatcher_Listeners__\r
+ * @class __Listeners__\r
  * @private\r
  * @abstract\r
  */\r
-var\r
-       /** @enum {number} */\r
-       X_Listeners_ =\r
-               /** @lends __X_EventDispatcher_Listeners__ */\r
-               {\r
-                       ACTUAL_HANDLER : 0,\r
-                       DISPATCHING    : 1,\r
-                       RESERVES       : 2,\r
-                       UNLISTENS      : 3,\r
-                       KILL_RESERVED  : 4 // X.Event で、イベントIDを 5 から始めているので注意。\r
-               };\r
+var X_Listeners_;\r
+\r
+var /** @const */\r
+       X_LISTENERS_ACTUAL_HANDLER = 0,\r
+       /** @const */\r
+       X_LISTENERS_DISPATCHING    = 1,\r
+       /** @const */\r
+       X_LISTENERS_RESERVES       = 2,\r
+       /** @const */\r
+       X_LISTENERS_UNLISTENS      = 3,\r
+       /** @const */\r
+       X_LISTENERS_KILL_RESERVED  = 4; // X.Event で、イベントIDを 5 から始めているので注意。\r
 \r
 \r
 \r
@@ -200,7 +201,7 @@ var X_EventDispatcher = X[ 'EventDispatcher' ] =
                                        cbHash = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
                                };\r
                                \r
-                               if( ( unlistens = listeners[ X_Listeners_.UNLISTENS ] ) && ( unlistens = unlistens[ opt_type ] ) ){\r
+                               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.supplement === cbHash.supplement && f.lock === lock ) ) return false;\r
@@ -267,21 +268,21 @@ function X_EventDispatcher_dispatch( e ){
        e[ 'target' ]        = e[ 'target' ] || this;\r
        e[ 'currentTarget' ] = e[ 'currentTarget' ] || this;\r
        \r
-       if( listeners[ X_Listeners_.DISPATCHING ] ){\r
-               ++listeners[ X_Listeners_.DISPATCHING ];\r
+       if( listeners[ X_LISTENERS_DISPATCHING ] ){\r
+               ++listeners[ X_LISTENERS_DISPATCHING ];\r
        } else {\r
-               listeners[ X_Listeners_.DISPATCHING ] = 1;\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
+       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
                if( !unlistens ){\r
-                       unlistens = listeners[ X_Listeners_.UNLISTENS ][ type ];\r
+                       unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
                };\r
                if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
                \r
@@ -290,25 +291,25 @@ function X_EventDispatcher_dispatch( e ){
                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
+                               unlistens = listeners[ X_LISTENERS_UNLISTENS ] || ( listeners[ X_LISTENERS_UNLISTENS ] = {} );\r
                                unlistens = unlistens[ type ] || ( unlistens[ type ] = [] );\r
                        };\r
                        unlistens.indexOf( f ) === -1 && ( unlistens[ unlistens.length ] = f );\r
                };\r
                ret |= X_Type_isFinite( r ) ? r : 0;\r
                \r
-               if( r & X_Callback_STOP_NOW ){\r
+               if( r & X_Callback_STOP_NOW === X_Callback_STOP_NOW ){\r
                        sysOnly = true;\r
                        break;\r
                };              \r
        };\r
        \r
-       if( ( --listeners[ X_Listeners_.DISPATCHING ] ) === 0 ){\r
+       if( ( --listeners[ X_LISTENERS_DISPATCHING ] ) === 0 ){\r
 \r
-               delete listeners[ X_Listeners_.DISPATCHING ];\r
+               delete listeners[ X_LISTENERS_DISPATCHING ];\r
                \r
                // dispatch 中に listen されたイベントの追加\r
-               if( list = listeners[ X_Listeners_.RESERVES ] ){\r
+               if( list = listeners[ X_LISTENERS_RESERVES ] ){\r
                        for( i = 0, l = list.length; i < l; ++i ){\r
                                f = list[ i ];\r
                                X_EventDispatcher_once = f[ 4 ];\r
@@ -318,12 +319,12 @@ function X_EventDispatcher_dispatch( e ){
                        };\r
                        list.length = 0;\r
                        X_EventDispatcher_once = X_EventDispatcher_lock = false;\r
-                       delete listeners[ X_Listeners_.RESERVES ];\r
+                       delete listeners[ X_LISTENERS_RESERVES ];\r
                };              \r
                \r
                // dispatch 中に unlisten されたイベントの削除\r
-               if( unlistens = listeners[ X_Listeners_.UNLISTENS ] ){\r
-                       delete listeners[ X_Listeners_.UNLISTENS ];\r
+               if( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ){\r
+                       delete listeners[ X_LISTENERS_UNLISTENS ];\r
                        \r
                        // _unlistens に入っている callbackHash は、lock をクリアしている\r
                        X_EventDispatcher_unlock = true;\r
@@ -343,7 +344,7 @@ function X_EventDispatcher_dispatch( e ){
                        delete X_EventDispatcher_LAZY_TIMERS[ X_Timer_currentUID ];\r
                };\r
 \r
-               if( listeners[ X_Listeners_.KILL_RESERVED ] ){\r
+               if( listeners[ X_LISTENERS_KILL_RESERVED ] ){\r
                        /*\r
                        for( timerID in X_EventDispatcher_LAZY_TIMERS ){\r
                                if( X_EventDispatcher_LAZY_TIMERS[ timerID ] === this ) return ret;\r
@@ -393,9 +394,9 @@ function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){
 \r
        if( !type ) return this;\r
        \r
-       if( listeners && listeners[ X_Listeners_.DISPATCHING ] ){\r
-               if( !listeners[ X_Listeners_.RESERVES ] ) listeners[ X_Listeners_.RESERVES ] = [];\r
-               listeners[ X_Listeners_.RESERVES ][ listeners[ X_Listeners_.RESERVES ].length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ];\r
+       if( listeners && listeners[ X_LISTENERS_DISPATCHING ] ){\r
+               if( !listeners[ X_LISTENERS_RESERVES ] ) listeners[ X_LISTENERS_RESERVES ] = [];\r
+               listeners[ X_LISTENERS_RESERVES ][ listeners[ X_LISTENERS_RESERVES ].length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ];\r
                return this;\r
        };\r
        \r
@@ -457,12 +458,12 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){
                return this;\r
        };\r
 \r
-       if( reserves = listeners[ X_Listeners_.RESERVES ] ){\r
+       if( reserves = listeners[ X_LISTENERS_RESERVES ] ){\r
                for( i = reserves.length; i; ){\r
                        f = reserves[ --i ];\r
                        if( f[ 0 ] === opt_type && f[ 1 ] === opt_arg1 && f[ 2 ] === opt_arg2 && f[ 3 ] === opt_arg3 && ( !f[ 5 ] || X_EventDispatcher_unlock ) ){\r
                                reserves.splice( i, 1 );\r
-                               if( !reserves.legth ) delete listeners[ X_Listeners_.RESERVES ];\r
+                               if( !reserves.legth ) delete listeners[ X_LISTENERS_RESERVES ];\r
                                return this;\r
                        };\r
                };\r
@@ -475,11 +476,11 @@ 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( 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
+                       ( listeners[ X_LISTENERS_UNLISTENS ][ opt_type ] = [ f ] );\r
        } else {\r
                delete f.once;\r
                list.splice( i, 1 );\r
@@ -490,7 +491,7 @@ function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){
                        // TODO カウンター\r
                        empty = true;\r
                        for( k in listeners ){\r
-                               if( k <= X_Listeners_.KILL_RESERVED ) continue;\r
+                               if( k <= X_LISTENERS_KILL_RESERVED ) continue;\r
                                empty = false;\r
                                break;\r
                        };\r
@@ -519,7 +520,7 @@ function X_EventDispatcher_unlistenAll( that ){
        \r
        for( type in listeners ){\r
                //if( X_EMPTY_OBJECT[ opt_type ] ) continue;\r
-               if( type <= X_Listeners_.KILL_RESERVED ) continue;\r
+               if( type <= X_LISTENERS_KILL_RESERVED ) continue;\r
                list = listeners[ type ];\r
                for( i = list.length; i; ){\r
                        that[ 'unlisten' ]( type, list[ --i ] );\r
@@ -528,7 +529,7 @@ function X_EventDispatcher_unlistenAll( that ){
 };\r
 \r
 function X_EventDispatcher_addEvent( that, type, raw, list ){\r
-       var i;\r
+       var i, f;\r
        X_EventDispatcher_lock || ( type = X_Event_Rename[ type ] || type );\r
        \r
        if( X_Type_isArray( type ) ){\r
@@ -537,21 +538,16 @@ function X_EventDispatcher_addEvent( that, type, raw, list ){
                        console.log( 'events fix > ' + type[ i ] );\r
                };\r
        } else {\r
-               X_EventDispatcher_actualAddEvent( that, type, raw, list );\r
-       };\r
-};\r
-\r
-var X_EventDispatcher_actualAddEvent =\r
+               \r
        // Days on the Moon DOM Events とブラウザの実装 \r
        // http://nanto.asablo.jp/blog/2007/03/23/1339502\r
        // Safari 2 では関数オブジェクトしか EventListener として使えませんが、Safari のナイトリービルドでは handleEvent メソッドを持つオブジェクトも EventListener として使えるようです。\r
-       X_UA_EVENT.W3C ?\r
-               (function( that, type, raw, list ){\r
-                       var f;\r
+               \r
+               if( X_UA_EVENT.W3C ){\r
                        switch( that[ '_rawType' ] ){\r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
                                        list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
-                                       list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
+                                       list.sltoken    = raw[ 'AddEventListener' ]( type, list.slcallback );\r
                                        break;\r
                                \r
                                case X_EventDispatcher_EVENT_TARGET_XHR :\r
@@ -571,7 +567,7 @@ var X_EventDispatcher_actualAddEvent =
                                                  type === 'animationiteration'  || type === 'webkitAnimationIteration' ) ){\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_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+                                               f = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
                \r
                                                if( raw.addEventListener ){\r
                                                        raw.addEventListener( type, f, false );\r
@@ -581,14 +577,12 @@ var X_EventDispatcher_actualAddEvent =
                                                };\r
                                        };\r
                        };\r
-               }) :\r
-       X_UA_EVENT.IE ?\r
-               (function( that, type, raw, list ){\r
-                       var f;\r
+               } else\r
+               if( X_UA_EVENT.IE ){\r
                        switch( that[ '_rawType' ] ){   \r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
                                        list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
-                                       list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
+                                       list.sltoken    = raw[ 'AddEventListener' ]( type, list.slcallback );\r
                                        break;                          \r
                                \r
                                case X_EventDispatcher_EVENT_TARGET_XHR :\r
@@ -598,7 +592,7 @@ var X_EventDispatcher_actualAddEvent =
                                        break;\r
                                \r
                                default :\r
-                                       f = that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+                                       f = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
                                        \r
                                        if( raw.attachEvent ){\r
                                                raw.attachEvent( 'on' + type, f );\r
@@ -607,13 +601,12 @@ var X_EventDispatcher_actualAddEvent =
                                        };\r
                                        break;\r
                        };\r
-               }) :\r
-               (function( that, type, raw, list ){\r
+               } else {\r
                        switch( that[ '_rawType' ] ){\r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
                                        // DOM0 で Silverlight ってあるの -> ie4 mobile?\r
                                        list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
-                                       list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
+                                       list.sltoken    = raw[ 'AddEventListener' ]( type, list.slcallback );\r
                                        break;                          \r
                                \r
                                case X_EventDispatcher_EVENT_TARGET_XHR :\r
@@ -622,14 +615,17 @@ var X_EventDispatcher_actualAddEvent =
                                        break;\r
 \r
                                default :\r
-                                       raw[ 'on' + type ] = that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+                                       raw[ 'on' + type ] = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
                                        break;\r
                        };\r
-               });\r
+               }\r
+       };\r
+};\r
+\r
 \r
 /*\r
  * iOS の webkitTransitionEnd が連続して起こる場合、\r
- * コールバックの(that[ X_Listeners_.ACTUAL_HANDLER ])クロージャ内の実際のコールバック(X_Callback_actualClosure:obj._)が\r
+ * コールバックの(that[ X_LISTENERS_ACTUAL_HANDLER ])クロージャ内の実際のコールバック(X_Callback_actualClosure:obj._)が\r
  * 参照できていない問題に遭遇、、、iOS3.1.3 & iOS6.1.5 で確認\r
  * animation も怪しい、、、\r
  */\r
@@ -654,16 +650,10 @@ function X_EventDispatcher_removeEvent( that, type, raw, list, skip ){
                        X_EventDispatcher_systemUnlisten( that, type[ --i ], X_emptyFunction );\r
                };\r
        } else {\r
-               X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip );\r
-       };\r
-};\r
-\r
-var X_EventDispatcher_actualRemoveEvent =\r
-       X_UA_EVENT.W3C ?\r
-               (function( that, type, raw, list, skip ){\r
+               if( X_UA_EVENT.W3C ){\r
                        switch( that[ '_rawType' ] ){\r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
-                                       raw.RemoveEventListener( type, list.sltoken ); // token\r
+                                       raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
                                        X_Callback_correct( list.slcallback );\r
                                        delete list.sltoken;\r
                                        delete list.slcallback;\r
@@ -686,22 +676,21 @@ var X_EventDispatcher_actualRemoveEvent =
                                                raw.removeEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false );\r
                                        } else                  \r
                                        if( raw.addEventListener ){\r
-                                               raw.removeEventListener( type, that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ], false );\r
+                                               raw.removeEventListener( type, that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ], false );\r
                                        } else {\r
                                                raw[ 'on' + type ] = null;\r
                                        };\r
                                        \r
-                                       if( !skip && that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] ){\r
-                                               X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
-                                               delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+                                       if( !skip && that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] ){\r
+                                               X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+                                               delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
                                        };\r
                        };\r
-               }) :\r
-       X_UA_EVENT.IE ?\r
-               (function( that, type, raw, list, skip ){\r
+               } else\r
+               if( X_UA_EVENT.IE ){\r
                        switch( that[ '_rawType' ] ){\r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
-                                       raw.RemoveEventListener( type, list.sltoken ); // token\r
+                                       raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
                                        X_Callback_correct( list.slcallback );\r
                                        delete list.sltoken;\r
                                        delete list.slcallback;\r
@@ -716,22 +705,21 @@ var X_EventDispatcher_actualRemoveEvent =
 \r
                                default :\r
                                        if( raw.attachEvent ){\r
-                                               raw.detachEvent( 'on' + type, that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
+                                               raw.detachEvent( 'on' + type, that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
                                        } else {\r
                                                raw[ 'on' + type ] = X_emptyFunction;\r
                                                raw[ 'on' + type ] = '';\r
                                        };\r
                                        \r
                                        if( !skip ){\r
-                                               X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
-                                               delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+                                               X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+                                               delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
                                        };\r
                        };\r
-               }) :\r
-               (function( that, type, raw, list, skip ){\r
+               } else {\r
                        switch( that[ '_rawType' ] ){\r
                                case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
-                                       raw.RemoveEventListener( type, list.sltoken ); // token\r
+                                       raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
                                        X_Callback_correct( list.slcallback );\r
                                        delete list.sltoken;\r
                                        delete list.slcallback;\r
@@ -748,11 +736,13 @@ var X_EventDispatcher_actualRemoveEvent =
                                        raw[ 'on' + type ] = '';\r
                                        \r
                                        if( !skip ){\r
-                                               X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
-                                               delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+                                               X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+                                               delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
                                        };\r
                        };\r
-               });\r
+               };\r
+       };\r
+};\r
 \r
 \r
 // TODO ブラウザからの呼び出しの最後に登録された関数を呼び出す機能(例えば画面の更新)\r
@@ -832,7 +822,7 @@ function X_EventDispatcher_toggleAllEvents( that, add ){
        if( !list || !raw ) return;\r
        for( type in list ){\r
                //if( X_EMPTY_OBJECT[ type ] ) continue;\r
-               //if( type <= X_Listeners_.KILL_RESERVED ) continue;\r
+               //if( type <= X_LISTENERS_KILL_RESERVED ) continue;\r
                // 数字イベントの除外\r
                if( !X_String_isNumberString( type ) ){\r
                        // TODO type rename はここ\r
index d2540f4..5a4ad5f 100644 (file)
@@ -46,6 +46,8 @@ X_ViewPort = X_Class_override(
                                        return X_ViewPort[ 'dispatch' ]( X_EVENT_BEFORE_UNLOAD );
                                        
                                case 'unload' :
+                                       //https://developer.mozilla.org/ja/docs/Web/JavaScript/A_re-introduction_to_JavaScript
+                                       //Firefox 1.5 の bfcache が無効になりますので、他に理由がない限り Firefox では unload リスナを登録するべきではないことに注意してください。
                                        X_ViewPort[ 'dispatch' ]( X_EVENT_UNLOAD );
                                //alert('unload');
                                        X_ViewPort_document[ 'kill' ]();
@@ -304,12 +306,27 @@ X[ 'ViewPort' ] = {
 
                        X_ViewPort_rootElement = document.compatMode !== 'CSS1Compat' ? elmBody : elmHtml || elmBody;
 
-                       html = X[ 'Doc' ][ 'html' ] = X_Node_html = elmHtml && new Node( elmHtml )[ 'removeClass' ]( 'js-disabled' )[ 'addClass' ]( X_UA_classNameForHTML );
+       /**
+        * Node( documentElement )
+        * @alias X.Doc.html
+        * @type {Node}
+        */
+                       X[ 'Doc' ][ 'html' ] = html = X_Node_html = elmHtml && new Node( elmHtml )[ 'removeClass' ]( 'js-disabled' )[ 'addClass' ]( X_UA_classNameForHTML );
                        html[ '_flags' ] |= X_Node_State.IN_TREE;
-                       
-                       head = X[ 'Doc' ][ 'head' ] = X_Node_head = elmHead && new Node( elmHead );
-               
-                       body = X[ 'Doc' ][ 'body' ] = X_Node_body = new Node( elmBody );
+
+       /**
+        * Node( head )
+        * @alias X.Doc.head
+        * @type {Node}
+        */                     
+                       X[ 'Doc' ][ 'head' ] = head = X_Node_head = elmHead && new Node( elmHead );
+
+       /**
+        * Node( documentElement )
+        * @alias X.Doc.body
+        * @type {Node}
+        */             
+                       X[ 'Doc' ][ 'body' ] = body = X_Node_body = new Node( elmBody );
 
                        body[ 'parent ' ] = head[ 'parent' ] = html;
                        html[ '_xnodes' ] = [ head, body ];
index 48251a6..457bdc1 100644 (file)
@@ -4,6 +4,10 @@
  * @alias X.Doc
  */
 X[ 'Doc' ] = {
+       /**
+        * EventDispatcher.prototype.listen 参照
+        * @alias X.Doc.listen
+        */
        'listen' : function( type, arg1, arg2, arg3 ){
                if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){
                        /*
@@ -15,7 +19,10 @@ X[ 'Doc' ] = {
                return X[ 'Doc' ];
        },
        
-       
+       /**
+        * EventDispatcher.prototype.listenOnce 参照
+        * @alias X.Doc.listenOnce
+        */
        'listenOnce' : function( type, arg1, arg2, arg3 ){
                if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){
                        /*
@@ -26,12 +33,20 @@ X[ 'Doc' ] = {
                type && arg1 && X_ViewPort_document[ 'listenOnce' ]( type, arg1, arg2, arg3 );
                return X[ 'Doc' ];
        },
-       
+
+       /**
+        * EventDispatcher.prototype.unlisten 参照
+        * @alias X.Doc.unlisten
+        */     
        'unlisten' : function( type, arg1, arg2, arg3 ){
                type && arg1 && X_ViewPort_document[ 'unlisten' ]( type, arg1, arg2, arg3 );
                return X[ 'Doc' ];
        },
-       
+
+       /**
+        * EventDispatcher.prototype.listening 参照
+        * @alias X.Doc.listening
+        */     
        'listening' : function( type, arg1, arg2, arg3 ){
                return X_ViewPort_document[ 'listening' ]( type, arg1, arg2, arg3 );
        },
@@ -39,11 +54,7 @@ X[ 'Doc' ] = {
        'create'     : X_Doc_create,
        
        'createText' : X_Doc_createText
-       
-       // html
-       // head
-       // body
-       // find
+
 };
 
 /**
index 3f9a059..9a33207 100644 (file)
@@ -148,13 +148,18 @@ function X_Node_Attr_objToAttrText( that, skipNetworkForElmCreation ){
 })( X_Node_Attr_renameForDOM, X_Node_Attr_renameForTag );\r
 \r
 \r
-\r
-/* --------------------------------------\r
- *  attribute\r
- * \r
- * http://nanto.asablo.jp/blog/2005/10/29/123294\r
- * className, onclick等 はここで設定しない\r
- * \r
+/**\r
+ * 属性の getter と setter。onclick等はできないので listen, listenOnce を使うこと。http://nanto.asablo.jp/blog/2005/10/29/123294\r
+ * @alias Node.prototype.attr\r
+ * @param {string|object} [nameOrObj] 属性名、または追加する属性のハッシュ\r
+ * @param {string|number} [value=] 属性の値\r
+ * @return {Node|string|number} getter の場合は値を、setter の場合は自身を返す。(メソッドチェーン)\r
+ * @example // getter\r
+ * node.attr( 'tagName' ) === 'DIV';\r
+ * // setter - 1\r
+ * node.attr( { src : url, width : 100, height : 100 } );\r
+ * // setter - 2\r
+ * node.attr( 'src', url );\r
  */\r
 Node.prototype[ 'attr' ] = function( nameOrObj /* v */ ){\r
        var attrs = this[ '_attrs' ], newAttrs, f, k, elm, v;\r
index ce4a789..9a9d85b 100644 (file)
@@ -504,8 +504,20 @@ function X_Node_CSS__splitValueAndUnit( v ){
 // unitID, name 単位指定のプロパティ取得 geter
 // obj setter
 // name, value setter
-
-Node.prototype[ 'css' ] = function( nameOrObj /* orUnitID, valuOrUnitOrName */ ){
+/**
+ * style の getter と setter。
+ * @alias Node.prototype.css
+ * @param {string|object} [nameOrObj] style 名、または追加する style のハッシュ
+ * @param {string|number} [value=] style の値
+ * @return {Node|string|number} getter の場合は値を、setter の場合は自身を返す。(メソッドチェーン)
+ * @example // getter
+ * node.css( 'color' );
+ * // setter - 1
+ * node.css( { width : w + 'px', height : h + 'px' } );
+ * // setter - 2
+ * node.css( 'color', 0x666666 );
+ */
+Node.prototype[ 'css' ] = function( nameOrObj /* value */ ){
        var args = arguments,
                css  = this[ '_css' ],
                p, name, v, plain, camelize, flags;
@@ -543,7 +555,7 @@ Node.prototype[ 'css' ] = function( nameOrObj /* orUnitID, valuOrUnitOrName */ )
        };
 // getter
        if( !css ) return;
-       // 集計 border, padding, margin, backgroundPosition, clip
+       // TODO 集計 border, padding, margin, backgroundPosition, clip
        // TODO border で正確なデータを返せない時は、null を返す
        return css[ X_Node_CSS_camelize( nameOrObj ) ];
 };
index a1b6fbd..00b0013 100644 (file)
@@ -56,9 +56,6 @@ var
        // TODO { a : 1, A : 2, _ : 3,,, }\r
        X_Node_Selector__ALPHABET  = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789\\',\r
        X_Node_Selector__NUMBER    = '+-0123456789';\r
-\r
-// XMLWrapper のために今だけ外部に公開\r
-X_NodeSelector_parse = X_Node_Selector__parse;\r
                \r
 /*\r
  * セレクタ文字列の解析、但し一挙に行わず、ひと塊づつ\r
@@ -228,7 +225,13 @@ function X_Node_Selector__parse( query, last ){
        return not ? [ i + tmp + 1, [ combinator, 6, result ] ] : [ i, result ];\r
 };\r
 \r
-       // セレクター\r
+       /**\r
+        * selector を使って Node を取得する\r
+        * @alias X.Doc.find\r
+        * @function\r
+        * @param {string} セレクター文字列\r
+        * @return {Node|NodeList}\r
+        */\r
        X[ 'Doc' ][ 'find' ] = X_shortcutFunction = Node.prototype[ 'find' ] = X_NodeList.prototype[ 'find' ] = function ( queryString ){\r
                var HTML      = X_Node_html,\r
                        scope     = this.constructor === X_NodeList && this.length ? this : [ this.constructor === Node ? this : X_Node_body ],\r
index 0b5a8a8..3913195 100644 (file)
@@ -1,26 +1,26 @@
 \r
 var\r
        ease = {\r
-               quadratic: {\r
+               'quadratic' : {\r
                        style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\r
                        fn: function (k) {\r
                                return k * ( 2 - k );\r
                        }\r
                },\r
-               circular: {\r
+               'circular' : {\r
                        style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',       // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)\r
                        fn: function (k) {\r
                                return Math.sqrt( 1 - ( --k * k ) );\r
                        }\r
                },\r
-               back: {\r
+               'back' : {\r
                        style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\r
                        fn: function (k) {\r
                                var b = 4;\r
                                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;\r
                        }\r
                },\r
-               bounce: {\r
+               'bounce' : {\r
                        style: '',\r
                        fn: function (k) {\r
                                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {\r
@@ -34,7 +34,7 @@ var
                                }\r
                        }\r
                },\r
-               elastic: {\r
+               'elastic' : {\r
                        style: '',\r
                        fn: function (k) {\r
                                var f = 0.22,\r
@@ -79,6 +79,7 @@ var X_Node_ANIMATIONS            = [],
 // 中断\r
 \r
 /*\r
+ * TODO : DX Anime\r
  * TODO : scale,  ActiveX transform, zoom, fontSizeScale\r
  * TODO : rotate, ActiveX transform -> 位置補正のために size 情報が必要なので、commitUpdate 後に計算して適用\r
  * TODO : matrix\r
@@ -92,20 +93,20 @@ Node.prototype[ 'animate' ] = function( start, dest, duration, easing, wait ){
                obj   = this[ '_anime' ] || ( this[ '_anime' ] = {} );\r
        \r
        obj.duration  = 0 <= duration && X_Type_isFinite( duration ) ? duration : 500;\r
-       obj.easing    = ease[ easing ] || ease.circular;\r
+       obj.easing    = ease[ easing ] || ease[ 'circular' ];\r
        // 現在 GPUレイヤーのtop になっているか?将来については phase で判定\r
        obj.gpuParent = obj.gpuParent || false;\r
        obj.phase     = duration === 0 ? 9 : obj.phase === 9 ? 9 : 0; //\r
        obj.wait      = X_Type_isFinite( wait ) ? wait : 1000;\r
                \r
        obj.startTime = X_Timer_now();\r
-       obj.startX    = ( start.x || start.x === 0 ) ? start.x : obj.x || 0;\r
-       obj.startY    = ( start.y || start.y === 0 ) ? start.y : obj.y || 0;\r
+       obj.startX    = ( start.x || start.x === 0 ) ? start.x : obj.x || NaN;\r
+       obj.startY    = ( start.y || start.y === 0 ) ? start.y : obj.y || NaN;\r
        obj.startA    = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : obj.a || 1;\r
        \r
        obj.destTime  = obj.startTime + obj.duration;\r
-       obj.destX     = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || 0;\r
-       obj.destY     = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || 0;\r
+       obj.destX     = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || NaN;\r
+       obj.destY     = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || NaN;\r
        obj.destA     = 0 <= dest.opacity && dest.opacity <= 1 ? dest.opacity : obj.destA || 1;\r
 \r
        X_Node_ANIMATIONS.indexOf( this ) === -1 &&\r
@@ -489,14 +490,15 @@ function X_Node_Anime_updatePosition( xnode, x, y, opacity, useGPU ){
        if( X_Node_Anime_hasTransform ){\r
                xnode[ 'css' ]({\r
                        transform : 'translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)' + ( useGPU ? X_Node_Anime_translateZ : '' ),\r
-                       opacity   : opacity\r
+                       opacity   : opacity === 1 ? '' : opacity\r
                });\r
        } else {\r
-               xnode[ 'css' ]({\r
+               x === x && xnode[ 'css' ]({\r
                        left    : ( x | 0 ) + 'px',\r
+                       opacity : opacity === 1 ? '' : opacity });\r
+               y === y && xnode[ 'css' ]({\r
                        top     : ( y | 0 ) + 'px',\r
-                       opacity : opacity\r
-               });\r
+                       opacity : opacity === 1 ? '' : opacity });\r
        };\r
 \r
        if( X_Node_Anime_translateZ ){\r
index be90379..a7858e4 100644 (file)
@@ -21,7 +21,7 @@ var X_Pulgin_FLASH_VERSION =
                        parseFloat( navigator.plugins[ 'Shockwave Flash' ].version ) :
                !X_UA[ 'IE4' ] && !X_UA[ 'IE5' ] && X_UA[ 'ActiveX' ] ? (function(){
                            var obj = eval( 'var a,e;try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}a' ),
-                               ver = obj && obj.GetVariable( '$version' ).split( ' ' ).join( '.' );
+                               ver = obj && obj[ 'GetVariable' ]( '$version' ).split( ' ' ).join( '.' );
                                return parseFloat( ver ) || 0;
                        })() :
                        0,
@@ -38,7 +38,7 @@ var X_Pulgin_FLASH_VERSION =
        X_Pulgin_SILVER_LIGHT_VERSION = 
                !X_UA[ 'IE' ] && navigator.plugins[ 'Silverlight Plug-In' ] ?
                        parseFloat( navigator.plugins[ 'Silverlight Plug-In' ].version ) :
-               X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] /* && !X_UA[ 'IECompat' ] */ ? (function(){
+               X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] ? (function(){
                            return eval( 'var a,i=0;try{a=new ActiveXObject("AgControl.AgControl");for(i=5;i;--i)if(a.IsVersionSupported(i+".0"))break;}catch(e){i=0}i' );
                        })() :
                        0,
diff --git a/0.6.x/js/05_util/04_XXML.js b/0.6.x/js/05_util/04_XXML.js
new file mode 100644 (file)
index 0000000..696606c
--- /dev/null
@@ -0,0 +1,575 @@
+/*\r
+ * XMLWrapper_find 周りの オリジナルコードに関する情報\r
+ *  Original code by pettanR team\r
+ *  - http://sourceforge.jp/projects/pettanr/scm/git/clientJs/blobs/master/0.6.x/js/01_dom/18_XDomQuery.js\r
+ *  and\r
+ *  Original code by ofk ( kQuery, ksk )\r
+ *  - http://d.hatena.ne.jp/ofk/comment/20090106/1231258010\r
+ *  - http://d.hatena.ne.jp/ofk/20090111/1231668170\r
+ */\r
+\r
+X[ 'XML' ] = XMLWrapper;\r
+\r
+function XMLWrapper( xml ){\r
+       this._rawXML = xml;\r
+};\r
+\r
+XMLWrapper.prototype.length = 1;\r
+XMLWrapper.prototype.has    = XMLWrapper_has;\r
+XMLWrapper.prototype.get    = XMLWrapper_get;\r
+XMLWrapper.prototype.val    = XMLWrapper_val;\r
+XMLWrapper.prototype.find   = XMLWrapper_find;\r
+\r
+function XMLWrapper_has( queryString ){\r
+       return !!this.find( queryString ).length;\r
+};\r
+\r
+function XMLWrapper_get( index ){\r
+       if( this.length === 1 ) return this;\r
+       if( this.length === 0 ) return null;\r
+       // 一度発行した XMLWrapper は控えて置いて再発行する。\r
+       if( this._wraps && this._wraps[ index ] ) return this._wraps[ index ];\r
+       if( !this._wraps ) this._wraps = [];\r
+       return this[ index ] ?\r
+               ( this._wraps[ index ] = new XMLWrapper( this[ index ] ) ) :\r
+               null;\r
+};\r
+\r
+function XMLWrapper_val( queryString, type ){\r
+       var attr_textContent = X.UA.IE < 9 || X.UA.Opera ? 'innerText' : X.UA.IE9 ? 'text' : 'textContent',\r
+               wrapper, xml, v;\r
+       \r
+       switch( queryString ){\r
+               case 'number' :\r
+               case 'int' :\r
+               case 'boolean' :\r
+               case 'string' :\r
+               case undefined :\r
+                       type = queryString;\r
+                       queryString = undefined;\r
+       };\r
+               \r
+       wrapper = queryString ? this.find( queryString ) : this;\r
+       xml     = wrapper.length === 1 ? wrapper._rawXML : wrapper[ 0 ];\r
+\r
+       if( !xml ){\r
+               switch( type ){\r
+                       case 'number' :\r
+                       case 'int' :\r
+                               return NaN;\r
+                       case 'boolean' :\r
+                               return false;\r
+                       case 'string' :\r
+                               return '';\r
+                       default :\r
+                               return null;\r
+               };\r
+       };\r
+       \r
+       v = xml.nodeType === 1 ? xml.innerText || xml.text || xml.textContent : xml.nodeValue;\r
+       //xml.toStrign()\r
+       switch( type ){\r
+               case 'number' :\r
+                       return parseFloat( v );\r
+               case 'int' :\r
+                       return parseInt( v );\r
+               case 'boolean' :\r
+                       return v && v !== '0' && v !== 'false' && v !== 'null' && v !== 'undefined' && v !== 'NaN';\r
+               //case 'string' :\r
+               //default :     \r
+       };\r
+       return v || '';\r
+};\r
+\r
+       function XMLWrapper_find( queryString ){\r
+\r
+               var scope     = this.constructor === XMLListWrapper ? this : [ this._rawXML ],\r
+                       parents   = scope, // 探索元の親要素 xmlList の場合あり\r
+                       ARY_PUSH  = Array.prototype.push,\r
+                       ret       = [], // 結果要素\r
+                       isXML     = true,\r
+                       isMulti   = 1 < scope.length,// 要素をマージする必要がある\r
+                       isStart   = true,\r
+                       _         = ' ',\r
+                       isAll, isNot, hasRoot,\r
+                       l, i, n, parsed,\r
+                       xmlList, // 一時保存用\r
+                       merge, // 要素がコメントノードで汚染されている場合使う\r
+                       combinator, selector, name, tagName,\r
+                       uid, tmp, xml, filter, key, op, val, toLower, useName,\r
+            links, className, attr, flag;\r
+\r
+               // 文字列以外は空で返す\r
+               if( !X_Type_isString( queryString ) ) return XMLListWrapper_0;\r
+               \r
+               xmlList = [];\r
+               \r
+               // 以下、パースと探索\r
+               for( ; queryString.length; ){\r
+                       //console.log( 'queryString[' + queryString + ']' );\r
+                       \r
+                       // 初期化処理\r
+                       if( !parsed ){\r
+                               parsed = X_Node_Selector__parse( queryString );\r
+                               \r
+                               if( X_Type_isNumber( parsed ) ){\r
+                                       // error\r
+                                       return XMLListWrapper_0;\r
+                               };\r
+                               \r
+                               queryString = queryString.substr( parsed[ 0 ] );\r
+                               parsed      = parsed[ 1 ];\r
+                               \r
+                               if( parsed === 5 ){\r
+                                       isMulti = true;\r
+                                       parents = scope;\r
+                                       xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );\r
+                                       parsed  = null;\r
+                                       xmlList = [];\r
+                                       isStart = true;\r
+                                       continue;\r
+                               };\r
+                       };\r
+                       \r
+                       combinator  = parsed[ 0 ];\r
+                       selector    = parsed[ 1 ];\r
+                       name        = parsed[ 2 ];\r
+                       tagName     = selector === 1 ? name : '*';\r
+                       isAll       = tagName === '*';\r
+       \r
+                       if( !isStart ){\r
+                               if( !xmlList.length ){\r
+                                       parsed = null;\r
+                                       continue;                                       \r
+                               } else\r
+                               if( combinator !== 0 ){\r
+                                       parents = xmlList;\r
+                                       xmlList = [];\r
+                                       //console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xmlList.length );\r
+                               };\r
+                       };\r
+                       \r
+                       i = 0;\r
+                       l = parents.length;\r
+                       n = -1; \r
+                       isMulti = isMulti || 1 < l;\r
+                       \r
+                       console.log( 'combinator ' + combinator );\r
+       \r
+                       switch( combinator ){\r
+                               // > TagName|*\r
+                               case 2 :\r
+                                       for( ; i < l; ++i ){\r
+                                               for( xml = parents[ i ].firstChild; xml; xml = xml.nextSibling ){\r
+                                                       if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ) xmlList[ ++n ] = xml;\r
+                                               };                              \r
+                                       };\r
+                                       break;\r
+                               // + TagName|*\r
+                               case 3 :\r
+                                       for( ; i < l; ++i ){\r
+                                               for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){\r
+                                                       if( xml.nodeType === 1 ){\r
+                                                               if( isAll || tagName === xml.tagName ) xmlList[ ++n ] = xml;\r
+                                                               break;                                                          \r
+                                                       };\r
+                                               };                                                              \r
+                                       };\r
+                                       break;\r
+                               // ~ TagName|*\r
+                               case 4 :\r
+                                       merge = [];\r
+                                       for( ; i < l; ++i ){\r
+                                               for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){\r
+                                                       if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ){\r
+                                                               if( merge.indexOf( xml ) !== -1 ){\r
+                                                                       break;\r
+                                                               } else {\r
+                                                                       merge[ merge.length ] = xml;\r
+                                                                       xmlList[ ++n ] = xml;\r
+                                                               };\r
+                                                       };                                                                      \r
+                                               };                                                              \r
+                                       };\r
+                                       break;\r
+\r
+                               // @ 属性ノード\r
+                               case 6 :\r
+                                       selector = 0;\r
+                                       tagName  = '*';\r
+                                       for( ; i < l; ++i ){\r
+                                               if( xml = parents[ i ].getAttributeNode( name ) ){\r
+                                                       xmlList[ ++n ] = xml;\r
+                                               };\r
+                                       };\r
+                                       break;\r
+                               default :\r
+                                       if( combinator === 1 || ( isStart && selector < 7 ) ){\r
+                                               console.log( l + ' > ' + xmlList.length + ' tag:' + tagName );\r
+                                               for( ; i < l; ++i ){\r
+                                                       xml = parents[ i ];\r
+                                                       xml.childNodes && xml.childNodes.length && XMLWrapper_fetchElements( xmlList, xml, isAll ? null : tagName );\r
+                                               };\r
+                                               console.log( l + ' >> ' + xmlList.length + ' tag:' + tagName );\r
+                                       };\r
+                       };\r
+                       \r
+                       isStart = false;\r
+                       \r
+                       //alert( 'pre-selector:' + ( xmlList && xmlList.length ) )\r
+                       \r
+                       switch( selector ){\r
+                               // #, ID\r
+                               case 2 :\r
+                                       filter = [ 'id', 1, name ]; break;\r
+                               // ., class\r
+                               case 3 :\r
+                                       filter = [ 'class', 3 /*'~='*/, name ]; break;\r
+                               // :, 擬似クラス\r
+                               case 4 :\r
+                                       if( !( filter = XMLWrapper_filter[ name ] ) ){\r
+                                               return XMLListWrapper_0;;\r
+                                       };\r
+                                       break;\r
+                               // [] 属性\r
+                               case 5 :\r
+                                       filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
+                               // :not\r
+                               case 6 :\r
+                                       isNot  = true;\r
+                                       parsed = parsed[ 2 ];\r
+                                       name   = parsed[ 2 ];\r
+                                       switch( parsed[ 1 ] ) {\r
+                                               case 1 :\r
+                                                       filter = [ 'tag', 1, name ]; break;\r
+                                               // #, ID\r
+                                               case 2 :\r
+                                                       filter = [ 'id', 1, name ]; break;\r
+                                               // ., class\r
+                                               case 3 :\r
+                                                       filter = [ 'class', 3, name ]; break;\r
+                                               // :, 擬似クラス\r
+                                               case 4 :\r
+                                                       if( !( filter = X_Node_Selector__filter[ name ] ) ){\r
+                                                               return [];\r
+                                                       };\r
+                                                       break;\r
+                                               // [] 属性\r
+                                               case 5 :\r
+                                                       filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
+                                       };\r
+                                       break;\r
+                               // scope\r
+                               case 7 :\r
+                                       xmlList = scope; break;\r
+                               /* root\r
+                               case 8 :\r
+                                       hasRoot = true;\r
+                                       xmlList = [ HTML ]; break;\r
+                               // link\r
+                               case 9 :\r
+                                       if( links = document.links ){\r
+                                               for( xmlList = [], i = links.length; i; ){\r
+                                                       xmlList[ --i ] = new Node( links[ i ] );\r
+                                               };\r
+                                       } else {\r
+                                               // area[href],a[href]\r
+                                       }; */\r
+                       };\r
+                       \r
+                       if( filter && xmlList.length ){\r
+                               // filter.mが関数の場合\r
+                               if( filter.m ){\r
+                                       xmlList = filter.m(\r
+                                               {\r
+                                                       not : isNot,\r
+                                                       xml : isXML\r
+                                               },\r
+                                               xmlList,\r
+                                               parsed[ 3 ], parsed[ 4 ]\r
+                                       );\r
+                               } else\r
+                               // filterが関数の場合\r
+                               if( X_Type_isFunction( filter ) ){\r
+                                       tmp = [];\r
+                                       for( i = 0, n = -1; xml = xmlList[ i ]; ++i ){\r
+                                               if( ( !!filter( xml ) ) ^ isNot ) tmp[ ++n ] = xml;     \r
+                                       };\r
+                                       xmlList = tmp;\r
+                               } else {\r
+                               // 属性セレクター                        \r
+                                       tmp = [];\r
+                                       key = filter[ 0 ];\r
+                                       op  = filter[ 1 ];\r
+                                       val = filter[ 2 ];\r
+\r
+                                       // 通常\r
+                                               if( op === 3 ) val = _ + val + _;\r
+\r
+                                               for( i = 0, n = -1, l = xmlList.length; i < l; ++i ){\r
+                                                       xml  = xmlList[ i ];\r
+                                                       attr = elem.getAttribute( key, 2 );\r
+                                                       flag = attr != null;// && ( !useName || attr !== '' );\r
+                                                       if( flag && op ){\r
+                                                               //if( toLower ) attr = attr.toLowerCase();\r
+                                                               \r
+                                                               switch( op ){\r
+                                                                       case 1: // =\r
+                                                                               flag = attr === val;\r
+                                                                               break;\r
+                                                                       case 2: // !=\r
+                                                                               flag = attr !== val;\r
+                                                                               break;\r
+                                                                       case 3: // ~=\r
+                                                                               flag = ( _ + attr + _ ).indexOf( val ) !== -1;\r
+                                                                               break;\r
+                                                                       case 4: // ^=\r
+                                                                               flag = attr.indexOf( val ) === 0;\r
+                                                                               break;\r
+                                                                       case 5: // $=\r
+                                                                               flag = attr.lastIndexOf( val ) + val.length === attr.length;\r
+                                                                               break;\r
+                                                                       case 6: // *=\r
+                                                                               flag = attr.indexOf( val ) !== -1;\r
+                                                                               break;\r
+                                                                       case 7: // |=\r
+                                                                               flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';\r
+                                                                               break;\r
+                                                               };\r
+                                                       };\r
+                                                       if( !!flag ^ isNot ) tmp[ ++n ] = xml;\r
+                                               //};\r
+                                       };\r
+                                       xmlList = tmp;\r
+                               };\r
+                       };\r
+                       filter  = null;\r
+                       isNot   = false;\r
+                       parsed  = null;\r
+                       \r
+                       //console.log( '//end :' + ( xmlList && xmlList.length ) );\r
+               };\r
+               //console.log( 'multi:' + ( xmlList && xmlList.length ) );\r
+               \r
+               // tree 順に並び替え、同一要素の排除\r
+               if( isMulti ){\r
+                       xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );\r
+                       l = ret.length;\r
+                       if( l === 0 ) return XMLListWrapper_0;\r
+                       if( l === 1 ) return new XMLWrapper( ret[ 0 ] );\r
+                       \r
+                       xmlList = [];\r
+                       //merge   = [];\r
+                       for( i = 0, n = -1; i < l; ++i ){\r
+                               //alert( 'multi:' + i )\r
+                               xml = ret[ i ];\r
+                               if( xmlList.indexOf( xml ) === -1 ){\r
+                                       //merge[ merge.length ] = xml;\r
+                                       xmlList[ ++n ] = xml;\r
+                               };\r
+                       };\r
+                       XMLWrapper_sortElementOrder( ret = [], xmlList, this._rawXML.childNodes );\r
+                       \r
+                       // @\r
+                       for( i = 0, l = xmlList.length; i < l; ++i ){\r
+                               if( ret.indexOf( xml = xmlList[ i ] ) === -1 ){\r
+                                       ret[ ret.length ] = xml;\r
+                               };\r
+                       };\r
+                       \r
+                       xmlList = ret;\r
+               };\r
+\r
+               return xmlList.length === 1 ? new XMLWrapper( xmlList[ 0 ] ) : new XMLListWrapper( xmlList );\r
+       };\r
+\r
+       function XMLWrapper_sortElementOrder( newList, list, xmlList ){\r
+               var l = xmlList.length,\r
+                       i = 0,\r
+                       j, child, _xmlList;\r
+               for( ; i < l; ++i ){\r
+                       child = xmlList[ i ];\r
+                       //if( child.nodeType !== 1 ) continue;\r
+                       //console.log( child.tagName );\r
+                       if( ( j = list.indexOf( child ) ) !== -1 ){\r
+                               newList[ newList.length ] = child;\r
+                               list.splice( j, 1 );\r
+                               if( list.length === 1 ){\r
+                                       newList[ newList.length ] = list[ 0 ];\r
+                                       list.length = 0;\r
+                                       return true;\r
+                               };\r
+                               if( list.length === 0 ) return true;\r
+                       };\r
+                       if( ( _xmlList = child.childNodes ) && XMLWrapper_sortElementOrder( newList, list, _xmlList ) ){\r
+                               return true;\r
+                       };\r
+               };\r
+       };\r
+       \r
+       function XMLWrapper_fetchElements( list, parent, tag ){\r
+               var xmlList = parent.childNodes,\r
+                       l      = xmlList.length,\r
+                       i      = 0,\r
+                       child;\r
+               for( ; i < l; ++i ){\r
+                       child = xmlList[ i ];\r
+                       if( child.nodeType === 1 ){\r
+                               ( !tag || child.tagName === tag ) && ( list[ list.length ] = child );\r
+                               //console.log( parent.tagName + ' > ' + child.tagName + ' == ' + tag+ ' l:' + list.length );\r
+                               child.childNodes && child.childNodes.length && XMLWrapper_fetchElements( list, child, tag );\r
+                       };\r
+               };\r
+       };\r
+\r
+       function XMLWrapper_funcSelectorChild( type, flag_all, flags, xmlList ){\r
+               var res      = [],\r
+                       flag_not = flags.not,\r
+                       i = 0, n = -1, xnode, node,\r
+                       tagName, tmp;\r
+               for( ; xnode = xmlList[ i ]; ++i ){\r
+                       tagName = flag_all || xnode.tagName;\r
+                       tmp     = null;\r
+                       if( /* tmp === null && */ type <= 0 ){\r
+                               for( node = xnode.previousSibling; node; node = node.previousSibling ){\r
+                                       if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+                                               tmp = false;\r
+                                               break;\r
+                                       };\r
+                               };\r
+                       };\r
+                       if( tmp === null && 0 <= type ){\r
+                               for( node = xnode.nextSibling; node; node = node.nextSibling ){\r
+                                       if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+                                               tmp = false;\r
+                                               break;\r
+                                       };              \r
+                               };                                              \r
+                       };\r
+                       if( tmp === null ) tmp = true;\r
+                       if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+               };\r
+               return res;\r
+       };\r
+       function XMLWrapper_funcSelectorNth( pointer, sibling, flag_all, flags, xmlList, a, b ){\r
+               var res      = [],\r
+                       checked  = {},\r
+                       flag_not = flags.not,\r
+                       i = 0, n = -1, uid,\r
+                       c, xnode, tmp, node, tagName;\r
+               for( ; xnode = xmlList[ i ]; ++i ){\r
+                       uid = xnode._uid;\r
+                       tmp = checked[ uid ];\r
+                       if( tmp === void 0 ){\r
+                               for( c = 0, node = xnode.parentNode[ pointer ], tagName = flag_all || xnode.tagName; node; node = node[ sibling ] ){\r
+                                       if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+                                               ++c;\r
+                                               checked[ node._uid ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;\r
+                                       };                                                      \r
+                               };\r
+                               tmp = checked[ uid ];\r
+                       };\r
+                       if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+               };\r
+               return res;\r
+       };\r
+       function XMLWrapper_funcSelectorProp( prop, flag, flags, xmlList ){\r
+               var res = [],\r
+                       flag_not = flag ? flags.not : !flags.not,\r
+                       i = 0, n = -1, xnode;\r
+               for( ; xnode = xmlList[ i ]; ++i ){\r
+                       if( xnode.getAttributeNode( prop ) ^ flag_not ) res[ ++n ] = xnode;\r
+               };\r
+               return res;\r
+       };\r
+\r
+var XMLWrapper_filter = {\r
+       'first-child' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, true, flags, xmlList ); }\r
+       },\r
+       'last-child' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, true, flags, xmlList ); }\r
+       },\r
+       'only-child' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, true, flags, xmlList ); }\r
+       },\r
+       'first-of-type' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, false, flags, xmlList ); }\r
+       },\r
+       'last-of-type' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, false, flags, xmlList ); }\r
+       },\r
+       'only-of-type' : {\r
+               m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, false, flags, xmlList ); }\r
+       },\r
+       'nth-child' : {\r
+               m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', true, flags, xmlList, a, b ); }\r
+       },\r
+       'nth-last-child' : {\r
+               m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', true, flags, xmlList, a, b ); }\r
+       },\r
+       'nth-of-type' : {\r
+               m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', false, flags, xmlList, a, b ); }\r
+       },\r
+       'nth-last-of-type' : {\r
+               m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', false, flags, xmlList, a, b ); }\r
+       },\r
+       empty : {\r
+               m : function( flags, xmlList ){\r
+                       var res = [],\r
+                               flag_not = flags.not,\r
+                               i = 0, n = -1, xnode, tmp, node;\r
+                       for( ; xnode = xmlList[i]; ++i ){\r
+                               tmp = true;\r
+                               for( node = xnode.firstChild; node; node = node.nextSibling ){\r
+                                       if( node.nodeType === 1 || ( node.nodeType === 3 && node._text ) ){\r
+                                               tmp = false;\r
+                                               break;\r
+                                       };                              \r
+                               };\r
+                               if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+                       };\r
+                       return res;\r
+               }\r
+       },\r
+       contains : {\r
+               m : function( flags, xmlList, arg ){\r
+                       var res = [],\r
+                               flag_not = flags.not,\r
+                               i = 0, n = -1, xnode, text = '',\r
+                               // kquery\r
+                               attr_textContent = X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ? 'innerText' : X_UA[ 'IE9' ] ? 'text' : 'textContent';\r
+                       for( ; xnode = xmlList[ i ]; ++i ){\r
+                               switch( xnode.nodeType ){\r
+                                       case 1 :\r
+                                               text = xml.nodeType === 1 ? xml.innerText || xml.text || xml.textContent : xml.nodeValue;// xnode[ attr_textContent ];\r
+                                               break;\r
+                                       case 2 :\r
+                                       case 3 :\r
+                                               text = xnode.nodeValue;\r
+                                               break;\r
+                               };\r
+                               console.log( text + ' ' + arg );\r
+                               if ( ( -1 < text.indexOf( arg ) ) ^ flag_not ) res[ ++n ] = xnode;                                              \r
+                       };\r
+                       return res;\r
+               }\r
+       }\r
+};\r
+\r
+function XMLListWrapper( xmlList ){\r
+       var i = 0, l = xmlList ? xmlList.length : 0;\r
+       for( ; i < l; ++i ){\r
+               this[ i ] = xmlList[ i ];\r
+       };\r
+       this.length = l;\r
+};\r
+\r
+var XMLListWrapper_0 = new XMLListWrapper();\r
+\r
+XMLListWrapper.prototype.length = 0;\r
+XMLListWrapper.prototype._wraps = null;\r
+XMLListWrapper.prototype.has    = XMLWrapper_has;\r
+XMLListWrapper.prototype.get    = XMLWrapper_get;\r
+XMLListWrapper.prototype.val    = XMLWrapper_val;\r
+XMLListWrapper.prototype.find   = XMLWrapper_find;\r
index 709f177..a97d45e 100644 (file)
@@ -7,9 +7,42 @@
 \r
 \r
 /**\r
- * 通信のキャンセル 通信中の場合は停止&破棄する kill(), busy() メソッドだけを持つ。X.Pair によって、プライベートメンバは隠蔽される。X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_ERROR, X_EVENT_TIMEOUT, X_EVENT_CANCELED\r
- * 通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。\r
- * SUCCESS, ERROR, TIMEOUT イベント以降は kill() は無視される。X.Net インスタンスは COMPLETE で自動で破棄される。\r
+ * <p>busy() メソッドだけを持つ。通信用のプロパティは X.Pair によって隠蔽される。\r
+ * <h4>通信のキャンセル</h4>\r
+ * <p>kill() で通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。SUCCESS, ERROR, TIMEOUT イベント以降は kill() は無視される。\r
+ * <h4>イベント</h4>\r
+ * <dl>\r
+ * <dt>X.Event.PROGRESS<dd>通信進行状況\r
+ * <dt>X.Event.SUCCESS<dd>通信成功\r
+ * <dt>X.Event.ERROR<dd>通信エラー\r
+ * <dt>X.Event.TIMEOUT<dd>通信タイムアウト\r
+ * <dt>X.Event.CANCELED<dd>通信のユーザー、プログラムによるキャンセル\r
+ * <dt>X.Event.COMPLETE<dd>通信完了。SUCCESS, ERROR, TIMEOUT, CANCELED 後に発生。\r
+ * </dl>\r
+ * <p>X.Net インスタンスは COMPLETE 後に自動で破棄される。\r
+ * <h4>必須プロパティ</h4>\r
+ * <dl>\r
+ * <dt>url<dd>URL\r
+ * <dt>type<dd>'xhr', 'jsonp', 'image', 'img'\r
+ * <dt>xhr<dd>URL { url : 'hoge', type : 'xhr' } の省略形\r
+ * <dt>jsonp<dd>URL { url : 'hoge', type : 'jsonp' } の省略形\r
+ * <dt>image, img<dd>URL { url : 'hoge', type : 'image' } の省略形\r
+ * </dl>\r
+ * <h4>XHR 用プロパティ</h4>\r
+ * <dl>\r
+ * <dt>method<dd>'GET', 'POST' 未指定かつ postdata を設定している場合、'POST' になる。\r
+ * <dt>postdata<dd>string, object の場合は X.String.serialize される。\r
+ * <dt>async<dd>boolean\r
+ * <dt>username<dd>BASIC 認証\r
+ * <dt>password<dd>BASIC 認証\r
+ * <dt>headers<dd>object xhr.setRequestHeader する値\r
+ * <dt>timeout<dd>タイムアウト ms\r
+ * <dt>cache<dd>headers[ 'Pragma' ] = 'no-cache' 等を設定するか?\r
+ * <dt>dataType<dd>'text', 'json', 'xml', 'blob', 'arraybuffer' 等。xhr.responseType に指定する値\r
+ * <dt>mimeType<dd>'text/xml', 'audio/mpeg' 等。xhr.overrideMimeType する値\r
+ * <dt>auth<dd>X.OAuth2 インスタンス(OAuth2 サービスの定義)\r
+ * </dl>\r
+ * \r
  * @alias X.Net\r
  * @class 各種ネットワーク機能をラップしインターフェイスを共通化する。\r
  * @constructs Net\r
@@ -34,6 +67,10 @@ X[ 'Net' ] = X_EventDispatcher[ 'inherits' ](
                'X.Net',\r
                X_Class.NONE,\r
                {\r
+                       'netType'    : '',\r
+                       'netName'    : '',\r
+                       'netVersion' : 0,\r
+                       \r
                        'Constructor' : function( urlOrObject, opt_options ){\r
                                var v, opt, url, type;\r
                                \r
@@ -231,8 +268,8 @@ function X_NET_shiftQueue(){
        switch( X_NET_currentData.netType ){\r
                case X_NET_TYPE_XHR :\r
                        \r
-                       // TODO xProtocol, method -> gadget.io.makeRequset, flash\r
-                       \r
+                       // TODO (xProtocol | method) & canUse -> gadget.io.makeRequset, flash\r
+                       // force 'gadget', 'flash'\r
                        X_NET_currentWrapper = X_NET_XHRWrapper || X_TEMP.X_Net_XHR_init();\r
                        \r
                        // OAuth2\r
index 490cb6c..843b5dc 100644 (file)
@@ -33,6 +33,13 @@ Android1.6- の XHR で 401 エラーが返った場合は、iframe に xml を
 IE9 で 画像バイナリの取得 VBA をかましている\r
 http://web.archive.org/web/20130808105151/http://gurimmer.lolipop.jp/daihakken/2012/05/22/javascriptajaxxmlhttprequest%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Fajax%E3%81%AE%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89\r
 http://d.hatena.ne.jp/maachang/20130221/1361427565\r
+\r
+http://web.archive.org/web/20130531162446/http://gurimmer.lolipop.jp/daihakken/2012/06/25/ajaxjavascript%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96xmlhttp%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E3%81%AF/\r
+\r
+IE8 以下で xhr の失敗率が高い問題 \r
+http://tkengo-totoro.blogspot.jp/2011/11/iexmlhttprequest.html\r
+TODO クライアント側にもリトライ機構を入れてみる\r
+\r
  */\r
 var // Opera7.6+, Safari1.2+, khtml3.?+, Gecko0.9.7+\r
        // ie7&8 ではローカルリソースには ActiveX の XHR を使う\r
@@ -44,7 +51,7 @@ var // Opera7.6+, Safari1.2+, khtml3.?+, Gecko0.9.7+
        X_Net_XHR_createXDR   = window[ 'XDomainRequest' ] && function(){ return X_Net_XHR_xdr || ( X_Net_XHR_xdr = new XDomainRequest() ); },\r
        X_Net_XHR_xdr         = X_Net_XHR_createXDR && X_Net_XHR_createXDR(),\r
 \r
-       // ie11の互換モード(7,8,5)の msxml はいまいち動かない\r
+       // ie11の互換モード(7,8)の msxml はいまいち動かない\r
        X_Net_XHR_createMSXML = X_UA[ 'ActiveX' ] && ( X_UA[ 'IE5x' ] || X_UA[ 'IE6' ] || X_URL_IS_LOCAL ) &&\r
                ( new Function( 'f', [\r
                        'var x=".XMLHTTP",',\r
@@ -74,7 +81,11 @@ if( X_Net_XHR_msXML ){
        X_Net_XHR_createMSXML = null;\r
 };\r
 \r
-X[ 'Net' ][ 'XHR' ] = {\r
+X[ 'XHR' ] = {\r
+\r
+       'W3C'         : X_Net_XHR_createW3C   ? 1 : 0,\r
+       'MSXML'       : X_Net_XHR_createMSXML ? 2 : 0,\r
+       'XDR'         : X_Net_XHR_createXDR   ? 4 : 0,\r
 \r
 /*\r
  * http://hakuhin.jp/as/import.html\r
@@ -82,20 +93,24 @@ X[ 'Net' ][ 'XHR' ] = {
  * http://hakuhin.jp/as/javascript.html\r
  * Flash から JavaScript にアクセスする(3+)\r
  */\r
-       'FLASH'       : false,\r
+       'FLASH'       : X_Pulgin_FLASH_ENABLED && 4 <= X_Pulgin_FLASH_VERSION ? 8 : 0,\r
+       \r
+       'GADGET'      : 5.5 <= X_UA[ 'IE' ] || !X_UA[ 'IE' ] ? 16 : 0,\r
 \r
 /**\r
  * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest\r
  * Progress Events     Chrome7, firefox3.5, ie10, opera12, Safari?, Chrome for Android 0.16\r
  */\r
-       'PROGRESS'    : X_Net_XHR_progress,\r
+       'PROGRESS'        : X_Net_XHR_progress,\r
 \r
-       'UL_PROGRESS' : X_Net_XHR_upload,\r
+       'UPLOAD_PROGRESS' : X_Net_XHR_upload,\r
 \r
-       // or gadget proxy\r
-       'CORS'        : X_Net_XHR_xdr || ( X_Net_XHR_w3c && X_Net_XHR_w3c.withCredentials !== undefined )\r
+       // or gadget proxy or flash\r
+       'CORS'            : X_Net_XHR_xdr || ( X_Net_XHR_w3c && X_Net_XHR_w3c.withCredentials !== undefined )\r
 };\r
 \r
+if( X_Net_XHR_msXMLVer ) X[ 'XHR' ][ 'MSXML_VERSION' ] = X_Net_XHR_msXMLVer;\r
+\r
 if( X_Net_XHR_w3c || X_Net_XHR_msXML ){\r
 \r
 X_TEMP.X_Net_XHR_init = function(){\r
@@ -139,7 +154,7 @@ X_TEMP.X_Net_XHR_init = function(){
                                this._dataType = obj[ 'dataType' ] || X_URL_getEXT( url );\r
                                \r
                                if( !raw || xDomain !== this._isXDR || ( X_Net_XHR_createMSXML && isFile !== this._isMsXML ) ){\r
-                                       this[ 'unlisten' ]( [ 'load', 'readystatechange', 'progress', 'error', 'timeout' ] );\r
+                                       raw && this[ 'unlisten' ]( [ 'load', 'readystatechange', 'progress', 'error', 'timeout' ] );\r
                                        init = true;\r
                                        this[ '_rawObject' ] = raw = xDomain ?\r
                                                                                                        X_Net_XHR_createXDR() :\r
@@ -220,6 +235,13 @@ X_TEMP.X_Net_XHR_init = function(){
                                };\r
 \r
                                if( !this._isXDR && ( this._isMsXML ? 3 <= X_Net_XHR_msXMLVer : raw.setRequestHeader ) ){ // msxml は setRequestHeader getter がいけない\r
+                                       \r
+                                       /*\r
+                                       if( noCache ){\r
+                                               headers[ 'Pragma' ] = 'no-cache';\r
+                                               headers[ 'Cache-Control' ] = 'no-cache';\r
+                                               headers[ 'If-Modified-Since' ] = 'Thu, 01 Jun 1970 00:00:00 GMT';\r
+                                       } else */                                       \r
                                        // http://nakigao.sitemix.jp/blog/?p=2040\r
                                        // json 取得時に SafariでHTTP/412のエラー。但し相手が audio の場合、この指定があるとロードに失敗する。 iOS8.2, iOS7.1 では遭遇せず\r
                                        if( this._dataType === 'json' ){\r
@@ -234,12 +256,7 @@ X_TEMP.X_Net_XHR_init = function(){
                                        if( method === 'POST' && !headers[ 'Content-Type' ] ){\r
                                                headers[ 'Content-Type' ] = 'application/x-www-form-urlencoded';\r
                                        };\r
-                                       /*\r
-                                       if( noCache ){\r
-                                               headers[ 'Pragma' ] = 'no-cache';\r
-                                               headers[ 'Cache-Control' ] = 'no-cache';\r
-                                               headers[ 'If-Modified-Since' ] = 'Thu, 01 Jun 1970 00:00:00 GMT';\r
-                                       }; */\r
+\r
                                        \r
                                        for( p in headers ){\r
                                                if( X_EMPTY_OBJECT[ p ] ) continue;\r
@@ -284,7 +301,7 @@ X_TEMP.X_Net_XHR_init = function(){
                        },\r
                        \r
                        cancel : function(){\r
-                               /* X.Net.XHR.CANCELABLE && */ this[ '_rawObject' ].abort && this[ '_rawObject' ].abort();\r
+                               /* this[ '_rawObject' ].abort && */ this[ '_rawObject' ].abort();\r
                                this._canceled = true;\r
                        },\r
                        \r
@@ -298,7 +315,7 @@ X_TEMP.X_Net_XHR_init = function(){
                                // XMLHttpRequest の使い方\r
                                // http://webos-goodies.jp/archives/50548720.html\r
                                // XMLHttpRequest オブジェクトを再利用する際も、 abort メソッドを呼び出す必要があるようです。\r
-                               this[ '_rawObject' ].abort && this[ '_rawObject' ].abort();     \r
+                               /* this[ '_rawObject' ].abort && */ this[ '_rawObject' ].abort();       \r
                                \r
                                // XMLHttpRequest で順番にリソースを取得する\r
                                // http://note.chiebukuro.yahoo.co.jp/detail/n16248\r
@@ -355,7 +372,7 @@ X_TEMP.X_Net_XHR_init = function(){
                                        http://www.semblog.org/msano/archives/000407.html\r
                                        * */            \r
                                        case 'readystatechange' :\r
-                                               //if( !X.Net.XHR.PROGRESS ){\r
+                                               //if( !X.XHR.PROGRESS ){\r
                                                        switch( raw.readyState ){\r
                                                                case 0 :\r
                                                                case 1 :\r
diff --git a/0.6.x/js/06_net/05_XXHRGadget.js b/0.6.x/js/06_net/05_XXHRGadget.js
new file mode 100644 (file)
index 0000000..61cb641
--- /dev/null
@@ -0,0 +1,176 @@
+/*\r
+ * gadgets.io.makeRequest\r
+ * \r
+ * 1. gadget-iframe を作る。指示を # で渡す。 元文書は frame 内の images の監視を開始する。\r
+ *  1. 通信用 img の src\r
+ *\r
+ * 2. gadget-iframe が 通信用 img を作る。#ready\r
+ * \r
+ * 3. 元文書が #ready を受け取ったら、iframe の # を書き換えて指示を送る。指示が長い場合、分割して送る。\r
+ * \r
+ * 4. gadget-iframe は 通信用 img の # に結果を書く。コンテンツが長い場合、分割する。\r
+ * \r
+ * 5. 元文書は結果を受け取ったことを gadget-iframe の # に書いて伝える。\r
+ * \r
+ * \r
+ */\r
+\r
+\r
+\r
+var X_NET_GIMR_canUse         = 5.5 <= X_UA[ 'IE' ] || !X_UA[ 'IE' ],\r
+       \r
+       X_NET_GIMR_iframeName     = 'gadgetProxy_' + ( Math.random() * 100000 | 0 ),\r
+       \r
+       X_NET_GIMR_GADGET_XML_URL = 'http://googledrive.com/host/0B4Y86MXyTfuoVUkwTE54T3V1V1U',\r
+       \r
+       X_NET_GIMR_GADGET_URL     = 'http://www.ig.gmodules.com/gadgets/ifr?url=' + encodeURIComponent( X_NET_GIMR_GADGET_XML_URL ) + '&nocache=1',\r
+       \r
+       X_NET_GIMR_IMAGE_URL      = 'img/opacity0.gif',\r
+       \r
+       X_NET_GIMR_detection      = new Function( 'f,j,i', 'for(j=f.length;j;)try{i=f[--j];return i.location.hash}catch(e){}' ),\r
+       \r
+       X_NET_GIMR_gadgetIframe,\r
+       \r
+       X_NET_GIMR_requestOptions,\r
+       \r
+       X_NET_GIMR_requestOriginal,\r
+       \r
+       X_NET_GIMR_timerID,\r
+       \r
+       X_NET_GIMR_phase = 0,\r
+       \r
+       X_NET_GIMR_lastHashString;\r
+\r
+\r
+function X_NET_GIMR_detectImageOverIframe(){\r
+       var raw = X_NET_GIMR_gadgetIframe[ '_rawObject' ],\r
+               iwin, ret;\r
+       \r
+       if( raw ){\r
+               iwin = raw.contentWindow || ( raw.contentDocument && raw.contentDocument.parentWindow ) || window.frames[ X_NET_GIMR_iframeName ];\r
+               \r
+               if( iwin && iwin.frames && iwin.frames.length ){\r
+                       ret = X_NET_GIMR_detection( iwin.frames );\r
+                       if( ret && ret !== X_NET_GIMR_lastHashString ){\r
+                               X_NET_GIMR_lastHashString = ret;\r
+                               console.log( ret );\r
+                               \r
+                               switch( X_NET_GIMR_phase ){\r
+                                       case 0 : // init\r
+                                               // TODO 分割\r
+                                               iwin.location.href = X_NET_GIMR_GADGET_URL + '#' + encodeURIComponent( X_NET_GIMR_toJSONString( X_NET_GIMR_requestOptions ) );\r
+                                               break;\r
+                                               \r
+                                       case 1 : // after makeRequest > :ok 待ち\r
+                                               iwin.location.href = X_NET_GIMR_GADGET_URL + '#_waiting_';\r
+                                               break;\r
+\r
+                                       case 2 : // _waiting_ 通信結果待ち\r
+                                               ret = decodeURIComponent( ret.substr( 1 ) );\r
+                                               ret = window.JSON ? JSON.parse( ret ) : eval( '(' + ret + ')' );\r
+                                               \r
+                                               X_NET_GIMRWrapper._busy = false;\r
+                                               \r
+                                               X_NET_GIMRWrapper\r
+                                                       [ 'asyncDispatch' ]( {\r
+                                                               type : ret[ 'errors' ] && ret[ 'errors' ].length ? X_EVENT_ERROR : X_EVENT_SUCCESS,\r
+                                                               data : ret\r
+                                                       } );\r
+                                               iwin.location.href = X_NET_GIMR_GADGET_URL + '#_recived_';\r
+                                               X_NET_GIMR_timerID = X_NET_GIMR_phase = 0;\r
+                                               X_NET_GIMR_lastHashString = '';\r
+                                               \r
+                                               return X_Callback_UN_LISTEN;\r
+                               };\r
+                               ++X_NET_GIMR_phase;\r
+                       };\r
+               };\r
+       };\r
+};\r
+\r
+// コマンドが長い場合、分割する\r
+function X_NET_GIMR_toJSONString( obj ){\r
+       var json = '', k, v;\r
+       for( k in obj ){\r
+               if( json ) json += ',';\r
+               v = obj[ k ];\r
+               v = v || v === 0 ? v : null;\r
+               json += '"' + k + '":' + ( X_Type_isObject( v ) ? X_NET_GIMR_toJSONString( v ) : X_Type_isString( v ) ? '"' + v + '"' : v );\r
+       };\r
+       console.log( json );\r
+       return '{' + json + '}';\r
+};\r
+\r
+\r
+// TODO extend NinjaIframe\r
+X_NET_GIMRWrapper = X_Class_override(\r
+       X_EventDispatcher(),\r
+       {\r
+\r
+               _busy         : false,\r
+               _canceled     : false,\r
+               _onloadCount  : 0,\r
+               \r
+               load : function( obj ){\r
+                       var k;\r
+                       //createURL\r
+                       if( !X_NET_GIMR_gadgetIframe ){\r
+                               X_NET_GIMR_gadgetIframe = X_Node_systemNode\r
+                                               .create( 'iframe', {\r
+                                                       className         : 'hidden-iframe',\r
+                                                       name              : X_NET_GIMR_iframeName,\r
+                                                       id                : X_NET_GIMR_iframeName,\r
+                                                       src               : X_NET_GIMR_GADGET_URL + '#' + encodeURIComponent(\r
+                                                               X_NET_GIMR_toJSONString( { 'img' : X_URL_toAbsolutePath( X_NET_GIMR_IMAGE_URL ), 'len' : 1000, 'itvl' : 200 } ) ),\r
+                                                       scrolling         : 'no',\r
+                                                       allowtransparency : 'no',                                       \r
+                                                       frameborder       : 0,\r
+                                                       tabindex          : -1\r
+                                                       } );\r
+                       };\r
+                       \r
+                       X_NET_GIMR_timerID = X.Timer.add( 100, 0, X_NET_GIMR_detectImageOverIframe );\r
+                       \r
+                       X_NET_GIMR_requestOriginal = X_Object_deepCopy( v );\r
+                       \r
+                       X_NET_GIMR_requestOptions = {\r
+                               'CONTENT_TYPE'     : 'TEXT',\r
+                               'GET_FULL_HEADERS' : true,\r
+                               'REFRESH_INTERVAL' : 0\r
+                       };\r
+                       \r
+                       for( k in obj ){\r
+                               if( v = obj[ k ] ){\r
+                                       switch( k ){\r
+                                               case 'postdata' :\r
+                                                       X_NET_GIMR_requestOptions[ 'POST_DATA' ] = v;\r
+                                                       break;\r
+                                               case 'method' :\r
+                                                       X_NET_GIMR_requestOptions[ 'METHOD' ] = v;\r
+                                                       break;                                  \r
+                                               case 'dataType' :\r
+                                                       X_NET_GIMR_requestOptions[ 'CONTENT_TYPE' ] = v;\r
+                                                       break;\r
+                                               case 'headers' :\r
+                                                       X_NET_GIMR_requestOptions[ 'HEADERS' ] = X_Object_clone( v );\r
+                                                       break;\r
+                                               case 'cashe' :\r
+                                                       X_NET_GIMR_requestOptions[ 'REFRESH_INTERVAL' ] = 3600;\r
+                                                       break;\r
+                                       };                                      \r
+                               };\r
+                       };\r
+                       \r
+                       this._busy = true;\r
+               },\r
+               \r
+               cancel : function(){\r
+                       this._canceled = true;\r
+               },\r
+               \r
+               reset : function(){\r
+                       this._busy = this._canceled = false;\r
+                       this._onloadCount = 0;\r
+               }\r
+       }\r
+);\r
diff --git a/0.6.x/js/06_net/10_XOAuth2.js b/0.6.x/js/06_net/10_XOAuth2.js
new file mode 100644 (file)
index 0000000..f11bb13
--- /dev/null
@@ -0,0 +1,312 @@
+
+var X_NET_OAUTH2_detection      = new Function( 'w', 'try{return w.location.search}catch(e){}' ),
+       X_NET_OAUTH2_authorizationWindow,
+       X_NET_OAUTH2_authorizationTimerID;
+
+/**
+ * イベント
+ * <dl>
+ * <dt>X.Event.NEED_AUTH<dd>window を popup して認可を行う必要あり。ポインターイベント内で oauth2.requestAuth() を呼ぶ。
+ * <dt>X.Event.CANCELED<dd>認可 window が閉じられた。([x]等でウインドウが閉じられた、oauth2.cancelAuth() が呼ばれた)
+ * <dt>X.Event.SUCCESS<dd>認可 window でユーザーが認可し、続いてコードの認可が済んだ。
+ * <dt>X.Event.ERROR<dd>コードの認可のエラー、リフレッシュトークンのエラー、ネットワークエラー
+ * <dt>X.Event.PROGRESS<dd>コードを window から受け取った、リフレッシュトークンの開始、コードの認可を header -> params に切替
+ * </dl>
+ * 
+ * OAuth2 state
+ * <dl>
+ * <dt>0 : <dd>disconnected
+ * <dt>1 : <dd>now authentication ...
+ * <dt>+ : <dd>authorization_code
+ * <dt>2 : <dd>refresh_token
+ * <dt>3 : <dd>hasAccessToken
+ * </dl>
+ */
+X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ](
+               'X.OAuth2',
+               X_Class.NONE,
+               
+               /** @lends OAuth2.prototype */
+               {
+                       'Constructor' : function( obj ){
+                               
+                               X_Pair_create( this, obj = X_Object_clone( obj ) );
+                               
+                               if( _getAccessToken( this ) ){
+                                       obj.oauth2State = 3;
+                               } else {
+                                       this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+                               }
+                               
+                               obj.onAuthError   = X_NET_OAUTH2_onXHR401Error;
+                               obj.updateRequest = X_NET_OAUTH2_updateRequest;
+                       },
+
+                       'authState' : function(){
+                               return X_Pair_get( this ).oauth2State || 0;
+                       },
+                       
+                       'requestAuth' : function(){
+                               // pointer event 内で呼ぶこと
+                               // 二つ以上の popup を作らない
+                               if( X_NET_OAUTH2_authorizationWindow ) return;
+                               
+                               pair = X_Pair_get( this );
+                               
+                               if( pair.net || pair.oauth2State ) return;
+                               
+                               X_NET_OAUTH2_authorizationWindow = window.open(
+                                       pair[ 'authorizeEndpoint' ] + '?' + X_URL_objToParam(
+                                               {
+                                                       response_type : 'code',
+                                                       client_id     : pair[ 'clientID' ],
+                                                       redirect_uri  : tpair[ 'redirectURI' ],
+                                                       scope         : ( pair[ 'scopes' ] || []).join(' ')
+                                               }
+                                       ), 'oauthauthorize',
+                                       'width=' + pair[ 'authorizeWindowWidth' ]
+                                       + ',height=' + pair[ 'authorizeWindowHeight' ]
+                                       + ',left=' + (screen.width  - pair[ 'authorizeWindowWidth'  ] ) / 2
+                                       + ',top='  + (screen.height - pair[ 'authorizeWindowHeight' ] ) / 2
+                                       + ',menubar=no,toolbar=no');
+                               
+                               X_NET_OAUTH2_authorizationTimerID = X_Timer_add( 333, 0, this, X_Net_OAuth2_detectAuthPopup );
+                               
+                               pair.oauth2State = 1;
+                               
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to auth.' } );
+                       },
+                       
+                       'cancelAuth' : function(){
+                               pair = X_Pair_get( this );
+                               
+                               if( pair.net ){
+                                       pair.net[ 'kill' ]();
+                                       delete pair.net;
+                               };
+                               
+                               X_NET_OAUTH2_authorizationWindow && X_NET_OAUTH2_authorizationWindow.close();
+                               X_NET_OAUTH2_authorizationWindow  = null;
+                               
+                               X_NET_OAUTH2_authorizationTimerID && X_Timer_remove( X_NET_OAUTH2_authorizationTimerID );
+                               X_NET_OAUTH2_authorizationTimerID = 0;
+                               
+                               this[ 'asyncDispatch' ]( X_EVENT_CANCELED );
+                       },
+                       
+                       'refreshToken' : function(){
+                               /*
+                                *                              var expires_at = this._getAccessTokenExpiry();
+                               if (expires_at && Date.now() + millis > expires_at)
+                                       this._refreshAccessToken({replay: false});
+                                */
+                               
+                               pair = X_Pair_get( this );
+                               
+                               if( pair.net ) return;
+                               
+                               pair.oauth2State = 2;
+                               
+                               pair.net = X.Net( {
+                                       'xhr'      : pair[ 'tokenEndpoint' ],
+                                       'postdata' : X_URL_objToParam({
+                                               'client_id'     : pair[ 'clientID' ],
+                                               'client_secret' : pair[ 'clientSecret' ],
+                                               'grant_type'    : 'refresh_token',
+                                               'refresh_token' : _getRefreshToken( this )
+                                       }),
+                                       'dataType' : 'json',
+                                       'headers' : {
+                                               'Accept'       : 'application/json',
+                                               'Content-Type' : 'application/x-www-form-urlencoded'
+                                       }
+                               } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_Net_OAuth2_responceHandler );
+                               
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to refresh token.' } );
+                       }
+               }
+       );
+
+function X_Net_OAuth2_detectAuthPopup(){
+       var closed, search, pair;
+       
+       if( window.frames[ 'oauthauthorize' ] !== X_NET_OAUTH2_authorizationWindow || X_NET_OAUTH2_authorizationWindow.closed ){
+               pair.oauth2State = 0;
+               closed = true;
+               this[ 'asyncDispatch' ]( X_EVENT_CANCELED );
+       } else
+       if( search = X_NET_OAUTH2_detection( X_NET_OAUTH2_authorizationWindow ) ){
+               pair      = X_Pair_get( this );
+               pair.code = X_URL_ParamToObj( search.slice( 1 ) )[ 'code' ];
+
+               X_NET_OAUTH2_authorizationWindow.close();
+               closed = true;
+
+               X_Net_OAuth2_authorizationCode( this, pair );
+               
+               this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Get code success, then authorization code.' } );
+       };
+       
+       if( closed ){
+               X_NET_OAUTH2_authorizationWindow  = null;
+               X_NET_OAUTH2_authorizationTimerID = 0;
+               
+               return X_Callback_UN_LISTEN;    
+       };
+};
+
+function X_Net_OAuth2_authorizationCode( oauth2, pair ){       
+       pair.net = X.Net( {
+               'xhr'      : pair[ 'tokenEndpoint' ],
+               'postdata' : X_URL_objToParam({
+                       'client_id'     : pair[ 'clientID' ],
+                       'client_secret' : pair[ 'clientSecret' ],
+                       'grant_type'    : 'authorization_code',
+                       'code'          : pair.code,
+                       'redirect_uri'  : pair[ 'redirectURI' ]
+               }),
+               'dataType' : 'json',
+               'headers' : {
+                       'Accept'       : 'application/json',
+                       'Content-Type' : 'application/x-www-form-urlencoded'
+               }
+       } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], oauth2, X_Net_OAuth2_responceHandler );
+};
+
+function X_Net_OAuth2_responceHandler( e ){
+       var data = e.data,
+               pair = X_Pair_get( this ),
+               isRefresh = pair.oauth2State === 2;
+       
+       delete pair.net;
+       
+       switch( e.type ){
+               case X_EVENT_SUCCESS :
+                       if( isRefresh && data.error ){
+                               _removeRefreshToken( this );
+                               pair.oauth2State = 0;
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' + data.error, data : data } );
+                               this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+                               return;
+                       } else
+                       if( data.error ){
+                               pair.oauth2State = 0;
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Get new access token error.' + data.error, data : data } );
+                               this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+                               return;
+                       };
+                       
+                       _setAccessToken( this, data[ 'access_token' ] || '' );
+                       _setRefreshToken( this, data[ 'refresh_token' ] || '' );
+                       
+                       if( data[ 'expires_in' ] ){
+                               _setAccessTokenExpiry( this, X_Timer_now() + data[ 'expires_in' ] * 1000 );
+                       } else
+                       if( _getAccessTokenExpiry( this ) ){
+                               _removeAccessTokenExpiry( this );
+                       };
+                       
+                       pair.oauth2State = 3;
+                       this[ 'asyncDispatch' ]( { type : X_EVENT_SUCCESS, message : isRefresh ? 'Refresh access token success.' : 'Get new access token success.' } );
+                       break;
+                       
+               case X_EVENT_ERROR :
+                       if( isRefresh ){
+                               // other error, not auth
+                               pair.oauth2State = 0;
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' } );
+                               _removeRefreshToken( this );
+                               this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+                       } else
+                       if( _getAuthMechanism( this ) === 'param' ){
+                               pair.oauth2State = 0;
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'network-error' } );
+                       } else {
+                               pair.oauth2State = 0;
+                               _setAuthMechanism( 'param' );
+                               this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Refresh access token failed. retry header -> param. ' } );
+                               // retry
+                               X_Net_OAuth2_authorizationCode( this, pair );
+                       };
+                       break;
+       };
+};
+
+function X_NET_OAUTH2_onXHR401Error( oauth2 ){
+       var pair = this,
+               xhr, bearerParams, headersExposed = false;
+       
+       if( _getAuthMechanism( oauth2 ) !== 'param' ){
+               xhr            = X_NET_currentWrapper[ '_rawObject' ];
+               bearerParams   = xhr.getResponseHeader( 'WWW-Authenticate' );
+               headersExposed = !X_Net_XHR_X_DOMAIN || !!xhr.getAllResponseHeaders(); // this is a hack for Firefox and IE
+       };
+       
+       // http://d.hatena.ne.jp/ritou/20110402/1301679908
+       if ( bearerParams && bearerParams.indexOf( ' error="' ) === -1 ) {
+               pair.oauth2State = 0;
+               oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+       } else
+       if ((( bearerParams && bearerParams.indexOf( ' error="invalid_token"' ) !== -1 ) || !headersExposed) && _getRefreshToken( oauth2 ) ) {
+               _removeAccessToken( oauth2 ); // It doesn't work any more.
+               pair.oauth2State = 2;
+               oauth2.refreshToken();
+       } else
+       if (!headersExposed && !_getRefreshToken( oauth2 )) {
+               pair.oauth2State = 0;
+               oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+       };
+};
+
+function X_NET_OAUTH2_updateRequest( oauth2, request ){
+       var token     = _getAccessToken( oauth2 ),
+               mechanism = _getAuthMechanism( oauth2 ),
+               url       = request[ 'url' ],
+               headers;
+
+       if( token && mechanism === 'param'){
+               request[ 'url' ] = url + ((url.indexOf('?') !== -1) ? '&' : '?') + 'bearer_token=' + encodeURIComponent( token );
+       };
+       
+       if( token && ( !mechanism || mechanism === 'header' ) ){
+               request[ 'headers' ] || ( headers = request[ 'headers' ] = {} );
+               headers[ 'Authorization' ] = 'Bearer ' + token;
+       };
+};
+
+function _getAccessToken( that ){ return updateLocalStorage( '', that, 'accessToken' ); }
+function _getRefreshToken( that){ return updateLocalStorage( '', that, 'refreshToken' ); }
+function _getAccessTokenExpiry( that ){ return updateLocalStorage( '', that, 'tokenExpiry' ); }
+function _getAuthMechanism( that ){
+               // IE's XDomainRequest doesn't support sending headers, so don't try.
+               return X_Net_XHR_X_DOMAIN ? 'param' : updateLocalStorage( '', that, 'AuthMechanism' );
+       }
+function _setAccessToken( that, value ){ updateLocalStorage( '+', that, 'accessToken' , value); }
+function _setRefreshToken( that, value ){ updateLocalStorage( '+', that, 'refreshToken', value); }
+function _setAccessTokenExpiry( that, value ){ updateLocalStorage( '+', that, 'tokenExpiry', value); }
+function _setAuthMechanism( that, value ){ updateLocalStorage( '+', that, 'AuthMechanism', value); }
+
+function _removeAccessToken( that ){ updateLocalStorage( '-', that, 'accessToken' ); }
+function _removeRefreshToken( that ){ updateLocalStorage( '-', that, 'refreshToken' ); }
+function _removeAccessTokenExpiry( that ){ updateLocalStorage( '-', that, 'tokenExpiry' ); }
+function _removeAuthMechanism( that ){ updateLocalStorage( '-', that, 'AuthMechanism' ); }
+       
+function updateLocalStorage( cmd, that, name, value ){
+       var action = cmd === '+' ? 'setItem' : '-' ? 'removeItem' : 'getItem',
+               pair;
+       
+       if( window.localStorage ){
+               return window.localStorage[ action ]( X_Pair_get( that )[ 'clientID' ] + name, value );
+       };
+       
+       pair = X_Pair_get( that );
+       switch( cmd ){
+               case '+' :
+                       pair[ name ] = value;
+                       break;
+               case '-' :
+                       if( pair[ name ] !== undefined ) delete pair[ name ];
+       };
+       return pair[ name ];
+};
+
index acae436..e923109 100644 (file)
        QuickTime   : 8,\r
  */\r
 \r
-var X_Audio_BACKENDS     = [], // Array.<Hash>\r
-       X_Audio_WRAPPER_LIST = []; // Array.<AudioWrapper>\r
-\r
-function X_Audio_getAudioWrapper( proxy ){\r
-       var i = X_Audio_WRAPPER_LIST.length;\r
-       for( ; i; ){\r
-               if( X_Audio_WRAPPER_LIST[ --i ].proxy === proxy ) return X_Audio_WRAPPER_LIST[ i ];\r
-       };\r
-};\r
+var X_Audio_BACKENDS     = []; // Array.<Hash>\r
 \r
 /*\r
  * X_EVENT_BACKEND_READY\r
@@ -40,13 +32,13 @@ function X_Audio_getAudioWrapper( proxy ){
  * X_EVENT_MEDIA_SEEKING シーク中に音声が待機状態に。間もなく X_EVENT_MEDIA_PLAYING に移行。\r
  */\r
 \r
+// TODO この内容は、AudioBackend の Abstract クラスにする。AudioSprite は Audio ではなく AudioBackend をマネージする\r
 X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ](\r
        'X.Audio',\r
        X_Class.POOL_OBJECT,\r
        {\r
                'source'      : '',\r
                'backendName' : '',\r
-               _backend      : -1,\r
                \r
                'Constructor' : function( sourceList, opt_option ){\r
                        X_Audio_startDetectionBackend(\r
@@ -57,41 +49,27 @@ X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ](
                },\r
                \r
                'play' : function( startTime, endTime, loop, loopStartTime, loopEndTime ){\r
-                       var state, duration;\r
-                       if( 0 <= startTime ){\r
-                               this[ 'state' ]( {\r
-                                       currentTime   : startTime,\r
-                                       startTime     : startTime,\r
-                                       endTime       : endTime,\r
-                                       loop          : loop,\r
-                                       loopStartTime : loopStartTime,\r
-                                       loopEndTime   : loopEndTime\r
-                               } );\r
-                       };\r
-                       this._backend !== -1 && X_Audio_getAudioWrapper( this ).play();\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.play( startTime, endTime, loop, loopStartTime, loopEndTime );\r
                        return this;\r
                },\r
                \r
                'seek' : function( seekTime ){\r
-                       var state = this[ 'state' ](),\r
-                               end   = X_AudioWrapper_getEndTime( X_Audio_getAudioWrapper( this ) );\r
-                       if( seekTime < end ){\r
-                               this[ 'state' ]( { currentTime : seekTime } );\r
-                       };\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.seek( seekTime );\r
                        return this;\r
                },\r
                \r
                'pause' : function(){\r
-                       this[ 'state' ]().playing && X_Audio_getAudioWrapper( this ).pause();\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.pause();\r
                        return this;\r
                },\r
                \r
                'state' : function( obj ){\r
-                       var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
-\r
+                       var pair = X_Pair_get( this );\r
                        if( obj === undefined ){\r
-                               return backend ?\r
-                                       backend.state() :\r
+                               return pair ? pair.getState() :\r
                                        {\r
                                        'startTime'     : -1,\r
                                        'endTime'       : -1,\r
@@ -102,58 +80,51 @@ X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ](
                                        'looded'        : false,\r
                                        'error'         : false,\r
                                        'playing'       : false,\r
-                                       \r
                                        'source'        : this[ 'source' ] || '',\r
                                        'duration'      : 0\r
                                        };\r
                        };\r
-                       backend && backend.state( obj );\r
+                       pair && pair.setState( obj );\r
                        return this;\r
                },              \r
                \r
                'loop' : function( v ){\r
-                       var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
-                       if( v === undefined ){\r
-                               return backend && backend.state().loop;\r
-                       };\r
-                       backend && backend.state( { loop : v } );\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.loop( v );\r
                        return this;\r
                },\r
 \r
                'volume' : function( v ){\r
-                       var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
-                       if( v === undefined ){\r
-                               return backend && backend.state().volume;\r
-                       };\r
-                       backend && backend.state( { volume : v } );\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.volume( v );\r
                        return this;\r
                },\r
 \r
                'currentTime' : function( v ){\r
-                       var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
-                       if( v === undefined ){\r
-                               return backend && backend.state().currentTime;\r
-                       };\r
-                       backend && backend.state( { currentTime : v } );\r
+                       var pair = X_Pair_get( this );\r
+                       pair && pair.currentTime( v );\r
                        return this;\r
                },\r
 \r
                'isPlaying' : function(){\r
-                       return this._backend !== -1 && X_Audio_getAudioWrapper( this ).state().playing;\r
+                       var pair = X_Pair_get( this );\r
+                       return pair && pair.playing;\r
                }\r
                \r
        }\r
 );\r
 \r
 function X_Audio_handleEvent( e ){\r
+       var backend;\r
+       \r
        switch( e.type ){\r
                case X_EVENT_BACKEND_READY :\r
+                       backend = X_Audio_BACKENDS[ e[ 'backendID' ] ];\r
+               \r
                        this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_Audio_handleEvent );\r
-                       this[ 'source' ]      = e.source;\r
-                       this[ 'backendName' ] = X_Audio_BACKENDS[ this._backend ].backendName;\r
-                       X_Audio_WRAPPER_LIST.push(\r
-                               new X_Audio_BACKENDS[ this._backend ]\r
-                               .klass( this, e.source, e.option ) );\r
+                       this[ 'source' ]      = e[ 'source' ];\r
+                       this[ 'backendName' ] = backend.backendName;\r
+                       X_Pair_create( this, backend.klass( this, e[ 'source' ], e[ 'option' ] ) );\r
                        break;\r
                \r
                case X_EVENT_BACKEND_NONE :\r
@@ -161,7 +132,9 @@ function X_Audio_handleEvent( e ){
                        break;\r
                \r
                case X_EVENT_KILL_INSTANCE :\r
-                       this._backend !== -1 && X_Audio_getAudioWrapper( this ).close();\r
+                       backend = X_Pair_get( this );\r
+                       backend && backend[ 'kill' ]();\r
+                       X_Pair_release( this, backend );\r
                        break;\r
        };\r
 };\r
@@ -171,126 +144,256 @@ function X_Audio_handleEvent( e ){
  * TODO preplayerror play してみたら error が出た、backend の変更。\r
  */\r
 \r
-function X_Audio_startDetectionBackend( backend, proxy, sourceList, option ){\r
+function X_Audio_startDetectionBackend( backend, xaudio, sourceList, option ){\r
        var source = sourceList[ 0 ] || '', \r
                ext    = X_URL_getEXT( source ),\r
                sup;\r
        \r
        if( source && backend ){\r
-               sup      = [ proxy, sourceList, option, source, ext ];\r
+               sup      = [ xaudio, sourceList, option, source, ext ];\r
                sup[ 5 ] = sup;\r
                \r
-               proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup );\r
-               backend.detect( proxy, source, ext );\r
+               xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup );\r
+               backend.detect( xaudio, source, ext );\r
        } else {\r
-               proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+               xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
        };\r
 };\r
 \r
-function X_Audio_onEndedDetection( e, proxy, sourceList, option, source, ext, sup ){\r
+function X_Audio_onEndedDetection( e, xaudio, sourceList, option, source, ext, sup ){\r
        var i = X_Audio_BACKENDS.indexOf( this ), backend;\r
        \r
        if( e.canPlay ){\r
-               proxy._backend = i;\r
-               proxy[ 'asyncDispatch' ]( {\r
+               xaudio._backend = i;\r
+               xaudio[ 'asyncDispatch' ]( {\r
                        type          : X_EVENT_BACKEND_READY,\r
                        'option'      : option,\r
                        'source'      : source,\r
-                       'backendName' : this[ 'backendName' ]\r
+                       'backendName' : this[ 'backendName' ],\r
+                       'backendID'   : i\r
                } );                    \r
        } else {\r
                console.log( 'No ' + source + ' ' + this[ 'backendName' ] );\r
                if( sup[ 3 ] = source = sourceList[ sourceList.indexOf( source ) + 1 ] ){\r
                        sup[ 4 ] = ext    = X_URL_getEXT( source );\r
-                       proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup );\r
-                       this.detect( proxy, source, ext );\r
+                       xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup );\r
+                       this.detect( xaudio, source, ext );\r
                } else\r
                if( backend = X_Audio_BACKENDS[ i + 1 ] ){\r
-                       X_Audio_startDetectionBackend( backend, proxy, sourceList, option );\r
+                       X_Audio_startDetectionBackend( backend, xaudio, sourceList, option );\r
                } else {\r
-                       proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+                       xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
                };                              \r
        };\r
 };\r
 \r
 \r
 \r
-function X_AudioWrapper_updateStates( audioWrapper, obj ){\r
-       var playing = audioWrapper.playing,\r
-               k, v,\r
-               end = 0, seek = 0, volume = 0;\r
-       \r
-       for( k in obj ){\r
-               v = obj[ k ];\r
-               switch( k ){\r
-                       case 'currentTime' :\r
-                               v = X_AudioWrapper_timeStringToNumber( v );\r
-                               if( X_Type_isNumber( v ) ){\r
-                                       if( playing ){\r
-                                               if( audioWrapper.state().currentTime !== v ){\r
-                                                       audioWrapper.seekTime = v;\r
-                                                       seek = 2;\r
+var X_Audio_AbstractAudioBackend = X_EventDispatcher[ 'inherits' ](\r
+       'X.AbstractAudioBackend',\r
+       X_Class.ABSTRACT,\r
+       {\r
+               \r
+               url           : '',\r
+               target        : null,\r
+               \r
+               startTime     : 0,\r
+               endTime       : -1,\r
+               loopStartTime : -1,\r
+               loopEndTime   : -1,\r
+               seekTime      : -1,\r
+               duration      : 0,\r
+\r
+               playing       : false,\r
+               error         : 0,                      \r
+               autoLoop      : false,\r
+               looped        : false,\r
+               autoplay      : false,\r
+               gain          : 0.5,\r
+               \r
+               play : function( startTime, endTime, loop, loopStartTime, loopEndTime ){\r
+                       if( 0 <= startTime ){\r
+                               this.setState( {\r
+                                       currentTime   : startTime,\r
+                                       startTime     : startTime,\r
+                                       endTime       : endTime,\r
+                                       loop          : loop,\r
+                                       loopStartTime : loopStartTime,\r
+                                       loopEndTime   : loopEndTime\r
+                               } );\r
+                       };\r
+                       this.actualPlay();\r
+               },\r
+               \r
+               seek : function( seekTime ){\r
+                       if( seekTime < X_AudioWrapper_getEndTime( this ) ){\r
+                               this.setState( { currentTime : seekTime } );\r
+                       };\r
+               },\r
+               \r
+               pause : function(){\r
+                       this.playing && this.actualPause();\r
+               },              \r
+               \r
+               loop : function( v ){\r
+                       if( v === undefined ){\r
+                               return this.autoLoop;\r
+                       };\r
+                       this.setState( { loop : v } );\r
+               },\r
+\r
+               volume : function( v ){\r
+                       if( v === undefined ){\r
+                               return this.gain;\r
+                       };\r
+                       this.setState( { volume : v } );\r
+               },\r
+\r
+               currentTime : function( v ){\r
+                       if( v === undefined ){\r
+                               return this.playing ? this.getActualCurrentTime() : this.seekTime;\r
+                       };\r
+                       this.setState( { currentTime : v } );\r
+               },\r
+               \r
+               getState : function(){\r
+                       \r
+                   return {\r
+                       'startTime'     : this.startTime,\r
+                       'endTime'       : this.endTime < 0 ? this.duration : this.endTime,\r
+                       'loopStartTime' : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
+                       'loopEndTime'   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
+                       'loop'          : this.autoLoop,\r
+                       'looped'        : this.looped,\r
+                       'volume'        : this.gain,\r
+                       'playing'       : this.playing,                         \r
+                       'duration'      : this.duration,\r
+                       \r
+                       'currentTime'  : this.playing ? this.getActualCurrentTime() : this.seekTime,\r
+                       'error'        : this.getActualError ? this.getActualError() : this.error\r
+                   };\r
+               },\r
+               \r
+               setState : function( obj ){\r
+                       var playing = this.playing,\r
+                               k, v,\r
+                               end = 0, seek = 0, volume = 0;\r
+                       \r
+                       for( k in obj ){\r
+                               v = obj[ k ];\r
+                               switch( k ){\r
+                                       case 'currentTime' :\r
+                                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                                               if( X_Type_isNumber( v ) ){\r
+                                                       if( playing ){\r
+                                                               if( this.getActualCurrentTime() !== v ){\r
+                                                                       seek = 2;\r
+                                                                       this.seekTime = v;\r
+                                                               };                                                              \r
+                                                       } else {\r
+                                                               this.seekTime = v;\r
+                                                       };\r
+                                               } else {\r
+                                                       continue;\r
                                                };\r
-                                       } else {\r
-                                               audioWrapper.seekTime = v;\r
-                                       };\r
-                               } else {\r
-                                       continue;\r
-                               };\r
-                               break;\r
+                                               break;\r
+                                                       \r
+                                       case 'startTime'     :\r
+                                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                                               if( v || v === 0 ){\r
+                                                       if( this.startTime !== v ){\r
+                                                               this.startTime = v;                                     \r
+                                                       };\r
+                                               } else {\r
+                                                       delete this.startTime;\r
+                                               };\r
+                                               break;\r
                                        \r
-                       case 'startTime'     :\r
-                       case 'endTime'       :\r
-                       case 'loopStartTime' :\r
-                       case 'loopEndTime'   :\r
-                               v = X_AudioWrapper_timeStringToNumber( v );\r
-                               console.log( k + ' ' + v );\r
-                               if( v || v === 0 ){\r
-                                       if( audioWrapper[ k ] !== v ){\r
-                                               audioWrapper[ k ] = v;\r
+                                       case 'endTime'       :\r
+                                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                                               if( v || v === 0 ){\r
+                                                       if( this.endTime !== v ){\r
+                                                               this.endTime = v;\r
+                                                               if( playing ) end = 1;                                          \r
+                                                       };\r
+                                               } else {\r
+                                                       delete this.endTime;\r
+                                                       if( playing ) end = 1;\r
+                                               };\r
+                                               break;\r
                                                \r
-                                               // 再生中の endTime の変更\r
-                                               if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;                                            \r
-                                       };\r
-                               } else {\r
-                                       delete audioWrapper[ k ];\r
-                                       if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;\r
-                               };\r
-                               break;\r
-\r
-                       case 'looped' :\r
-                               if( playing ) seek = 2;\r
-                       case 'loop' :\r
-                       case 'autoplay' :\r
-                               if( X_Type_isBoolean( v ) && audioWrapper[ k ] !== v ){\r
-                                       audioWrapper[ k ] = v;\r
+                                       case 'loopStartTime' :\r
+                                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                                               if( v || v === 0 ){\r
+                                                       if( this.loopStartTime !== v ){\r
+                                                               this.loopStartTime = v;                                 \r
+                                                       };\r
+                                               } else {\r
+                                                       delete this.loopStartTime;\r
+                                               };\r
+                                               break;\r
+                                               \r
+                                       case 'loopEndTime'   :\r
+                                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                                               if( v || v === 0 ){\r
+                                                       if( this.loopEndTime !== v ){\r
+                                                               this.loopEndTime = v;\r
+                                                               if( playing ) end = 1;                                          \r
+                                                       };\r
+                                               } else {\r
+                                                       delete this.loopEndTime;\r
+                                                       if( playing ) end = 1;\r
+                                               };\r
+                                               break;\r
+               \r
+                                       case 'looped' :\r
+                                               if( X_Type_isBoolean( v ) && this.looped !== v ){\r
+                                                       this.looped = v;\r
+                                                       if( playing ) seek = 2;\r
+                                               };\r
+                                               break;\r
+                                               \r
+                                       case 'loop' :\r
+                                               if( X_Type_isBoolean( v ) && this.autoLoop !== v ){\r
+                                                       this.autoLoop = v;\r
+                                               };\r
+                                               break;\r
+                                               \r
+                                       case 'autoplay' :\r
+                                               if( X_Type_isBoolean( v ) && this.autoplay !== v ){\r
+                                                       this.autoplay = v;\r
+                                               };\r
+                                               break;\r
+               \r
+                                       case 'volume' :\r
+                                               if( X_Type_isNumber( v ) ){\r
+                                                       v = v < 0 ? 0 : 1 < v ? 1 : v;\r
+                                                       if( this.gain !== v ){\r
+                                                               this.gain = v;\r
+                                                               // if playing -> update\r
+                                                               if( playing ) volume = 4;\r
+                                                       };\r
+                                               };\r
+                                               break;\r
                                };\r
-                               break;\r
+                       };\r
+                       \r
+                       if( this.endTime < this.startTime ||\r
+                               ( this.loopEndTime < 0 ? this.endTime : this.loopEndTime ) < ( this.loopStartTime < 0 ? this.startTime : this.loopStartTime ) ||\r
+                               X_AudioWrapper_getEndTime( this ) < this.seekTime// ||\r
+                               //this.duration < this.endTime\r
+                       ){\r
+                               console.log( 'setState 0:' + this.startTime + ' -> ' + this.endTime + ' d:' + this.duration + ' 1:' + this.loopStartTime + ' -> ' + this.loopEndTime );\r
+                               return;\r
+                       };\r
+                       \r
+                       v = end + seek + volume;\r
+                       return v && this.afterUpdateState( v );         \r
+               }\r
+               \r
+       }\r
+);\r
 \r
-                       case 'volume' :\r
-                               if( X_Type_isNumber( v ) ){\r
-                                       v = v < 0 ? 0 : 1 < v ? 1 : v;\r
-                                       if( audioWrapper[ k ] !== v ){\r
-                                               audioWrapper[ k ] = v;\r
-                                               // if playing -> update\r
-                                               if( playing ) volume = 4;\r
-                                       };\r
-                               };\r
-                               break;\r
-               };\r
-       };\r
-       \r
-       if( audioWrapper.endTime < audioWrapper.startTime ||\r
-               ( audioWrapper.loopEndTime < 0 ? audioWrapper.endTime : audioWrapper.loopEndTime ) < ( audioWrapper.loopStartTime < 0 ? audioWrapper.startTime : audioWrapper.loopStartTime ) ||\r
-               X_AudioWrapper_getEndTime( audioWrapper ) < audioWrapper.seekTime// ||\r
-               //audioWrapper.duration < audioWrapper.endTime\r
-       ){\r
-               console.log( 'error @updateStateObject() begin:' + audioWrapper.startTime + ' end:' + audioWrapper.endTime + ' d:' + audioWrapper.duration + ' ls:' + audioWrapper.loopStartTime );\r
-               return 0;\r
-       };\r
-       \r
-       return end + seek + volume;\r
-};\r
 \r
 function X_AudioWrapper_timeStringToNumber( time ){\r
        var ary, ms, s = 0, m = 0, h = 0;\r
index 958b441..00cf9c3 100644 (file)
@@ -2,6 +2,7 @@
 var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ]  && !X_UA[ 'iPad_2Mini1' ]  && !X_UA[ 'iPod_4' ]  &&
                                                                !( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) &&
                                                                ( window.AudioContext || window.webkitAudioContext ),
+       X_Audio_BUFFER_LIST      = [],
        X_Audio_WebAudioWrapper;
 
 /*
@@ -11,80 +12,34 @@ if( X_Audio_WebAudio_context ){
        
        X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
        
-       function X_Audio_WebAudio_getBuffer( url ){
-               var i = 0, l = X_Audio_WRAPPER_LIST.length;
-               for( i = 0; i < l; ++i ){
-                       if( X_Audio_WRAPPER_LIST[ i ].url === url ) return X_Audio_WRAPPER_LIST[ i ];
-               };
-       };
-       
-       X_Audio_WebAudioWrapper = X_EventDispatcher[ 'inherits' ](
-               'X.AV.WebAudioWrapper',
+       X_Audio_BufferLoader = X_EventDispatcher[ 'inherits' ](
+               'X.AV.WebAudioBufferLoader',
                X_Class.POOL_OBJECT,
                {
-                       
                        url             : '',
-                       proxy           : null,
-                       
-                       startTime       : 0,
-                       endTime         : -1,
-                       loopStartTime   : -1,
-                       loopEndTime     : -1,
-                       seekTime        : -1,
-                       duration        : 0,
-
-                       playing         : false,
-                       error           : 0,                    
-                       loop            : false,
-                       looped          : false,
-                       autoplay        : false,
-                       volume          : 0.5,
-                                               
-                       _startPos       : 0,
-                       _endPosition    : 0,
-                       _startTime      : 0,
-            _timerID        : 0,
-            _interval       : 0,
-               buffer          : null,
-               source          : null,
-            gainNode        : null,
-            _onended        : null,
-            
             xhr             : null,
             onDecodeSuccess : null,
             onDecodeError   : null,
             
-                       Constructor : function( proxy, url, option ){
-                               var audio = X_Audio_WebAudio_getBuffer( url );
-                               
-                               this.proxy  = proxy;
-                               this.url    = url;
-                               
-                               X_AudioWrapper_updateStates( this, option );
-                               
-                               if( audio && audio.buffer ){
-                                       this._onDecodeSuccess( audio.buffer );
-                               } else
-                               if( audio ){
-                                       // TODO 当てにしていたaudioがclose 等した場合
-                                       audio.proxy[ 'listenOnce' ]( 'canplaythrough', this, this._onBufferReady );
-                               } else {
-                                       this.xhr = X.Net( { 'xhr' : url, 'type' : 'arraybuffer' } )
+            buffer          : null,
+            error           : 0,
+            webAudioList    : null,
+            
+                       Constructor : function( webAudio, url ){
+                               this.webAudioList = [ webAudio ];
+                               this.url = url;
+                               this.xhr = X.Net( { 'xhr' : url, 'dataType' : 'arraybuffer' } )
                                                                        [ 'listen' ]( X_EVENT_PROGRESS, this )
-                                                                       [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );                                      
-                               };
+                                                                       [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
                        },
                        
                        handleEvent : function( e ){
                                switch( e.type ){
                                        case X_EVENT_PROGRESS :
-                                               e[ 'percent' ] ?
-                                                       this.proxy[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } ) :
-                                                       this.proxy[ 'dispatch' ]( 'loadstart' );
+                                               this[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } );
                                                return;
                                        
                                        case X_EVENT_SUCCESS :
-                                               console.log( 'WebAudio xhr success! ' + !!X_Audio_WebAudio_context.decodeAudioData + ' t:' + typeof e.data );
                                        // TODO 旧api
                                        // https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
                                        
@@ -104,17 +59,12 @@ if( X_Audio_WebAudio_context ){
                                                };
                                                break;
 
-                                       case X_EVENT_CANCELED :
-                                               this.error = 1;
-                                               this.proxy[ 'dispatch' ]( 'aborted' );
-                                               break;
-
                                        case X_EVENT_COMPLETE :
-                                               this.error = 2;                         
-                                               this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'xhr error' } );
+                                               this.error = 1;                         
+                                               this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                                                break;
                                };
-                               this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );
+                               this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
                                delete this.xhr;
                        },
                        
@@ -124,30 +74,23 @@ if( X_Audio_WebAudio_context ){
                                        this.onDecodeSuccess && this._onDecodeComplete();
                                        
                        if ( !buffer ) {
-                           this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'buffer is ' + buffer } );
+                               this.error = 2;
+                           this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                            return;
                        };
        
                        this.buffer   = buffer;
-                       this.duration = buffer.duration * 1000;
-                       /*
-                       this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );
-                       this.proxy[ 'asyncDispatch' ]( 'loadeddata' );
-                       this.proxy[ 'asyncDispatch' ]( 'canplay' );
-                       this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );
-                       */
-                                       this.proxy[ 'asyncDispatch' ]( X_EVENT_READY );
-                       
-                       this.autoplay && X_Timer_once( 16, this, this.play );
-                       
+
+                                       this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
+
                        console.log( 'WebAudio decoded!' );
                                },
                                
                                _onDecodeError : function(){
                                        console.log( 'WebAudio decode error!' );
                                        this._onDecodeComplete();
-                                       this.error = 3;
-                                       this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'decode error' } );
+                                       this.error = 2;
+                                       this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
                                },
                                
                                _onDecodeComplete : function(){
@@ -156,36 +99,100 @@ if( X_Audio_WebAudio_context ){
                                        X_Callback_correct( this.onDecodeError );
                                        delete this.onDecodeError;
                                },
-                               
-                               _onBufferReady : function( e ){
-                                       var audio = X_Audio_WebAudio_getBuffer( this.url );
-                                       this._onDecodeSuccess( audio.buffer );
-                               },
                        
-                       close : function(){     
-                   delete this.buffer;
-       
-                               if( this.xhr ) this.xhr.close();
-                               
-                               if( this.onDecodeSuccess ){
-                                       // 回収はあきらめる、、、
+                       unregister : function( webAudio ){
+                               var list = this.webAudioList,
+                                       i    = list.indexOf( webAudio );
+                               if( 0 < i ){
+                                       list.splice( i, 1 );
+                                       if( list.length ){
+                                               this.xhr && this.xhr[ 'kill' ]();
+                                               this[ 'kill' ]();
+                                       };
                                };
+                       }
+                       
+               }
+       );
        
-                               this.playing  && this.pause();
-                   this.source   && this._sourceDispose();
        
-                   this._onended && X_Callback_correct( this._onended );       
-       
-                   this.gainNode && this.gainNode.disconnect();
-                       },
+       X_Audio_WebAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
+               'X.AV.WebAudioWrapper',
+               X_Class.POOL_OBJECT,
+               {
                        
-                               _sourceDispose : function(){
-                           this.source.disconnect();
-                           delete this.source.onended;
-                           delete this.source;
-                       },
+                       loader          : null,
+                                               
+                       _startPos       : 0,
+                       _endPosition    : 0,
+                       _startTime      : 0,
+            _timerID        : 0,
+            _interval       : 0,
+               buffer          : null,
+               source          : null,
+            gainNode        : null,
+            _onended        : null,
+            
+                       Constructor : function( target, url, option ){                          
+                               var i = 0,
+                                       l = X_Audio_BUFFER_LIST.length,
+                                       loader;
+
+                               for( ; i < l; ++i ){
+                                       loader = X_Audio_BUFFER_LIST[ i ];
+                                       if( loader.url === url ){
+                                               this.loader = loader;
+                                               loader.webAudioList.push( this );
+                                               break;
+                                       };
+                               };
+                               
+                               if( !this.loader ){
+                                       this.loader = loader = new X_Audio_BufferLoader( this, url );
+                               };
+                               
+                               this.target  = target || this;
+                               
+                               this.setState( option );
+                               
+                               this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_WebAudio_handleEvent );
+                               
+                               if( loader.buffer || loader.error ){
+                                       this._onLoadBufferComplete();
+                               } else {
+                                       loader[ 'listenOnce' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+                               };
+                       },
+                               _onLoadBufferComplete : function( e ){
+                                       var loader = this.loader,
+                                               buffer = loader.buffer;
+                                       
+                                       e && loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+                                       
+                       if ( !buffer ) {
+                               this.error = loader.error;
+                               
+                           this.target[ 'dispatch' ]({
+                                                               type    : X_EVENT_ERROR,
+                                                               error   : loader.error,
+                                                               message : loader.error === 1 ?
+                                                                                       'load buffer network error' :
+                                                                                       'buffer decode error'
+                                                       });
+                                               this[ 'kill' ]();
+                           return;
+                       };
+       
+                       this.buffer   = buffer;
+                       this.duration = buffer.duration * 1000;
+
+                                       this.target[ 'asyncDispatch' ]( X_EVENT_READY );
+                       
+                       this.autoplay && X_Timer_once( 16, this, this.play );
+                                       
+                               },
                        
-                       play : function(){
+                       actualPlay : function(){
                                var begin, end;
                                
                    if( !this.buffer ){
@@ -207,7 +214,7 @@ if( X_Audio_WebAudio_context ){
                    this.source.buffer = this.buffer;
                    this.source.connect( this.gainNode );
                    
-                   this.gainNode.gain.value = this.volume;
+                   this.gainNode.gain.value = this.gain;
                    
                    // おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1
                    // 破棄された X.Callback が呼ばれて、obj.proxy() でエラーになる。Firefox では、onended は使わない
@@ -231,13 +238,19 @@ if( X_Audio_WebAudio_context ){
                    this._startTime   = X_Audio_WebAudio_context.currentTime * 1000;
                    this._interval    = this._interval || X_Timer_add( 1000, 0, this, this._onInterval );
                        },
+                       
+                               _sourceDispose : function(){
+                           this.source.disconnect();
+                           delete this.source.onended;
+                           delete this.source;
+                       },
 
                                _onInterval : function(){
                                        if( !this.playing ){
                                                delete this._interval;
                                                return X_Callback_UN_LISTEN;
                                        };
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
                                },
                                                
                                _onEnded : function(){
@@ -259,25 +272,25 @@ if( X_Audio_WebAudio_context ){
                                        };
                                };
                                
-                               if( this.loop ){
-                                       if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
+                               if( this.autoLoop ){
+                                       if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
                                                this.looped = true;
-                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
-                                               this.play();
+                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
+                                               this.actualPlay();
                                        };
                                } else {
-                                       this.pause();
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
+                                       this.actualPause();
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
                                };
                            };
                                },
                        
-                       pause : function(){
+                       actualPause : function(){
                                if( !this.playing ) return this;
                                
                                console.log( '[WebAudio] pause' );
                                
-                               this.seekTime = this.state().currentTime;
+                               this.seekTime = this.getActualCurrentTime();
                                
                    this._timerID && X_Timer_remove( this._timerID );
                                delete this._timerID;
@@ -290,40 +303,42 @@ if( X_Audio_WebAudio_context ){
                                this.source.stop( 0 ) : this.source.noteOff( 0 );
                    };
                        },
-       
-                       state : function( obj ){
-                               var result;
-                               
-                               if( obj === undefined ){
-                                   return {
-                                       startTime     : this.startTime,
-                                       endTime       : this.endTime < 0 ? this.duration : this.endTime,
-                                       loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
-                                       loopEndTime   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
-                                       loop          : this.loop,
-                                       looped        : this.looped,
-                                       volume        : this.volume,
-                                       playing       : this.playing,                           
-                                       duration      : this.duration,
-                                       
-                                       currentTime   : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0 ) : this.seekTime,
-                                       error         : this.error
-                                   };
-                               };
                        
-                               result = X_AudioWrapper_updateStates( this, obj );
-                               
+                       getActualCurrentTime : function(){
+                               return X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0;
+                       },
+                       
+                       afterUpdateState : function( result ){
                                if( result & 2 || result & 1 ){ // seek
-                       this.play();
+                       this.actualPlay();
                                } else
                                if( result & 4 ){
-                      this.gainNode.gain.value = this.volume;
+                      this.gainNode.gain.value = this.gain;
                                };
                        }
 
                }
        );
 
+       function X_WebAudio_handleEvent( e ){
+               switch( e.type ){
+
+                       case X_EVENT_KILL_INSTANCE :
+                               this.loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete )
+                                       .unregister( this );
+
+                               delete this.buffer;
+                               
+                               this.playing  && this.actualPause();
+                   this.source   && this._sourceDispose();
+       
+                   this._onended && X_Callback_correct( this._onended );       
+       
+                   this.gainNode && this.gainNode.disconnect();
+                               break;
+               };
+       };
+
        /*
         * http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1
         * L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。
index 6da87e2..fcf91f4 100644 (file)
@@ -10,7 +10,7 @@ var X_Audio_HTMLAudio_playTrigger =
                X_UA[ 'iOS' ]      ? 'suspend' :\r
                X_UA[ 'AndroidBrowser2' ] ? 'stalled' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,,\r
                X_UA[ 'AndroidBrowser4' ] ? 'loadeddata' : \r
-               X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : 'canplay',\r
+               X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : 'loadeddata', //'canplay',\r
        X_Audio_HTMLAudioWrapper,\r
        X_Audio_constructor = window[ 'Audio' ] || window.HTMLAudioElement,\r
        X_Audio_rawAudio,\r
@@ -30,7 +30,8 @@ var X_Audio_HTMLAudio_playTrigger =
        X_Audio_HTMLAudioWrapper_ieMobile9Fix    = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),\r
        X_Audio_HTMLAudioWrapper_durationFix     = ( !X_Audio_HTMLAudioWrapper_currentTimeFix && 12 <= X_UA[ 'Opera' ] ),\r
        \r
-       X_Audio_HTMLAudioWrapper_shortPlayFix    = X_UA[ 'AndroidBrowser2' ],\r
+       X_Audio_HTMLAudioWrapper_shortPlayFix    = //X_UA[ 'AndroidBrowser2' ],\r
+                                                                                               X_UA[ 'AndroidBrowser' ] && X_UA[ 'AndroidBrowserWebkit' ] < 534.3, // Android 4.1.1 でも遭遇\r
        \r
        X_Audio_codecs;\r
 \r
@@ -71,27 +72,10 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                };\r
        };\r
        \r
-       X_Audio_HTMLAudioWrapper = X_EventDispatcher[ 'inherits' ](\r
+       X_Audio_HTMLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](\r
                'X.AV.HTML5AudioWrapper',\r
                X_Class.POOL_OBJECT,\r
                {\r
-                       \r
-                       proxy           : null,\r
-                       \r
-                       startTime       : 0,\r
-                       endTime         : -1,\r
-                       loopStartTime   : -1,\r
-                       loopEndTime     : -1,\r
-                       seekTime        : -1,\r
-                       duration        : 0,\r
-                       \r
-                       playing         : false,\r
-                       error           : 0,                    \r
-                       loop            : false,\r
-                       looped          : false,\r
-                       autoplay        : false,\r
-                       volume          : 0.5,\r
-\r
                        _playTime        : 0,\r
                        _closed          : true,\r
                        _loaded          : false,\r
@@ -100,26 +84,26 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                        _lastCurrentTime : 0,\r
                        _src             : '',\r
                        \r
-                       Constructor : function( proxy, source, option ){\r
+                       Constructor : function( target, source, option ){\r
                                var raw;\r
                                \r
-                               this.proxy   = proxy;\r
+                               this.target  = target || this;\r
                                this._closed = false;\r
                                \r
-                               X_AudioWrapper_updateStates( this, option );\r
+                               this.setState( option );\r
 \r
                                if( option[ 'useVideo' ] ){\r
-                                       this[ '_rawObject' ] = raw = document.createElement( 'video' );\r
+                                       this[ '_rawObject' ]  = raw = document.createElement( 'video' );\r
                        raw.preload           = 'none'; // auto, metadata, none\r
                        //raw.autoplay = false, // no-auto\r
-                       raw.loop     = false;\r
-                       raw.muted    = false;\r
-                       //raw.crossorigin       = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え\r
+                       raw.loop              = false;\r
+                       raw.muted             = false;\r
+                       raw.crossorigin       = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え\r
                        raw.style.cssText     = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';\r
                                        raw.controls          = false;\r
                                        raw.WebKitPlaysInline = true;\r
                                        raw.src               = source;\r
-                                       //raw.onclick  = "alert('play');this.play();";\r
+                                       //raw.onclick  = "alert('play');this.actualPlay();";\r
                                        document.body.appendChild( raw );\r
                                        //raw.load();\r
                                } else {\r
@@ -159,6 +143,17 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                switch( e.type ){\r
 \r
                                        case X_EVENT_KILL_INSTANCE :\r
+                                               // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】\r
+                                               // http://ingaouhou.com/archives/3633\r
+                                               // ・使い終わったインスタンスはload()しておくとやや安定\r
+                                               this.playing && this.actualPause();\r
+                                               delete this._closed;\r
+                                               delete this._loaded;\r
+                                               \r
+                                               this[ '_rawObject' ].src = '';\r
+                                               this[ '_rawObject' ].load();\r
+                                               \r
+                                               // removeChild for video\r
                                                break;\r
                                };\r
                        },\r
@@ -170,7 +165,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                \r
                                X_Audio_HTMLAudioWrapper_badOperaAndroid && alert( e.type );\r
                                \r
-                               console.log( e.type );\r
+                               //console.log( e.type );\r
                                \r
                                switch( e.type ){\r
                                        case 'loadstart' :      //      ブラウザがコンテンツの検索を開始した場合に発生\r
@@ -230,11 +225,11 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                                break;\r
                                                \r
                                        case 'ended' :\r
-                                               if( !this._closed && this.loop ){\r
-                                                       if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+                                               if( !this._closed && this.autoLoop ){\r
+                                                       if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
                                                                this.looped = true;\r
-                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
-                                                               this.play();\r
+                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+                                                               this.actualPlay();\r
                                                        };\r
                                                        return;\r
                                                };\r
@@ -250,11 +245,6 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                                                if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){\r
                                                                        this.duration = this.duration || this[ '_rawObject' ].duration * 1000;\r
                                                                        this._playForDuration = 2;\r
-                                                                       \r
-                                                       //this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
-                                                       //this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
-                                                       //this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
-                                                       //this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );\r
                                                        loaded = true;\r
                                                        //console.log( 'durationFix が完了' + this.duration );\r
                                                        break;\r
@@ -264,8 +254,8 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                                                };\r
                                                        } else\r
                                                        if( this[ '_rawObject' ].currentTime === this._lastCurrentTime ){\r
-                                                               //this.proxy[ 'dispatch' ]( 'seeking' );\r
-                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+                                                               //this.target[ 'dispatch' ]( 'seeking' );\r
+                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
                                                                return;\r
                                                        };\r
                                                        this._lastCurrentTime = this[ '_rawObject' ].currentTime;\r
@@ -274,17 +264,18 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
 \r
                                    if( this.playing ){\r
                                        end = X_AudioWrapper_getEndTime( this );\r
-                                       now = X_Audio_HTMLAudioWrapper_currentTimeFix ? X_Timer_now() - this._playTime + this._beginTime : this[ '_rawObject' ].currentTime * 1000 | 0;\r
+                                       now = this.getActualCurrentTime();\r
+                                       //console.log( end + ' / ' + now );\r
                                        if( 0 + end <= 0 + now ){ // なぜか iem9 で必要,,,\r
-                                               if( this.loop ){\r
-                                                       if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+                                               if( this.autoLoop ){\r
+                                                       if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
                                                                this.looped = true;\r
-                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
-                                                               this.play();\r
+                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+                                                               this.actualPlay();\r
                                                        };\r
                                                } else {\r
-                                                       this.pause();\r
-                                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+                                                       this.actualPause();\r
+                                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
                                                };\r
                                                return;\r
                                        };\r
@@ -318,14 +309,7 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                                                        console.log( '設定 ' + this._beginTime );\r
                                                                        return;\r
                                                                };\r
-                                                               \r
-                                                               /*\r
-                                               this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
-                                               this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
-                                               this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
-                                               this.proxy[ 'asyncDispatch' ]( 'canplaythrough' ); */\r
-                                              \r
-                                               \r
+\r
                                                loaded = true;\r
                                                console.log( 'durationFix が完了' + this.duration );\r
                                                \r
@@ -348,27 +332,18 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                if( !this._loaded && ( loaded || e.type === X_Audio_HTMLAudio_playTrigger || e.type === 'loadeddata' ) ){\r
                                        this.autoplay && X_Timer_once( 16, this, this.play );\r
                                        this._loaded = true;\r
-                                       this.proxy[ 'dispatch' ]( X_EVENT_READY );\r
+                                       this.target[ 'asyncDispatch' ]( X_EVENT_READY );\r
                                        console.log( 'Loaded! ' + e.type + ' d:' + ( this.duration | 0 ) );\r
                                        return;\r
                                };\r
                                \r
-                               loaded || ( type && this.proxy[ 'dispatch' ]( type ) );\r
-                       },\r
-                       \r
-                       close : function(){\r
-                               // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】\r
-                               // http://ingaouhou.com/archives/3633\r
-                               // ・使い終わったインスタンスはload()しておくとやや安定\r
-                               this.playing && this.pause();\r
-                               delete this._closed;\r
-                               delete this._loaded;\r
-                               \r
-                               this[ '_rawObject' ].src = '';\r
-                               this[ '_rawObject' ].load();\r
+                               if( !loaded && type ){\r
+                                       this.target[ 'dispatch' ]( type );\r
+                                       type === X_EVENT_ERROR && this[ 'kill' ]();\r
+                               };\r
                        },\r
-                       \r
-                       play : function(){\r
+\r
+                       actualPlay : function(){\r
                                var begin, end;\r
                                \r
                                // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
@@ -397,14 +372,13 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                        delete this._playForDuration;\r
                                };\r
 \r
-                           \r
                            if( !this.playing ){\r
                                    if( X_UA[ 'Chrome' ] ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
                                        // [!] delay\r
                                        X_Timer_once( 0, this, this._fixForChrome );\r
                                        this[ '_rawObject' ].volume = 0;\r
                                    } else {\r
-                                       this[ '_rawObject' ].volume = this.volume;\r
+                                       this[ '_rawObject' ].volume = this.gain;\r
                                    };\r
                                this[ '_rawObject' ].play();\r
                                this.playing = true;\r
@@ -425,13 +399,13 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                                \r
                                // [CHROME][FIX] volume\r
                                _fixForChrome : X_UA[ 'Chrome' ] && function(){\r
-                                       !this._closed && ( this[ '_rawObject' ].volume = this.volume );\r
+                                       !this._closed && ( this[ '_rawObject' ].volume = this.gain );\r
                                },\r
                        \r
-                       pause : function(){\r
+                       actualPause : function(){\r
                                if( !this.playing ) return;\r
                                \r
-                               this.seekTime = this.state().currentTime;\r
+                               this.seekTime = this.getActualCurrentTime();\r
 \r
                                delete this._playTime;\r
 \r
@@ -443,55 +417,30 @@ if( X_Audio_constructor && !X_Audio_HTMLAudioWrapper_badOperaAndroid ){
                        };\r
                        delete this.playing;\r
                        },\r
-       \r
-                       state : function( obj ){\r
-                               var result;\r
-                               \r
-                               if( obj === undefined ){\r
-                                   return {\r
-                                       startTime     : this.startTime,\r
-                                       endTime       : this.endTime < 0 ? this.duration : this.endTime,\r
-                                       loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
-                                       loopEndTime   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
-                                       \r
-                                       loop          : this.loop,\r
-                                       looped        : this.looped,\r
-                                       volume        : this.volume,\r
-                                       playing       : this.playing, // && !this[ '_rawObject' ].error && !this[ '_rawObject' ].paused && !this[ '_rawObject' ].ended,                         \r
-                                       duration      : this.duration,\r
-                                       \r
-                                       currentTime   :\r
-                                               this.playing ?\r
-                                                       ( X_Audio_HTMLAudioWrapper_currentTimeFix ?\r
+                       \r
+                       getActualCurrentTime : function(){\r
+                               return ( X_Audio_HTMLAudioWrapper_currentTimeFix ?\r
                                                                X_Timer_now() - this._playTime + this._beginTime :\r
-                                                               this[ '_rawObject' ].currentTime * 1000 | 0 ) :\r
-                                                       this.seekTime,\r
-                                       /*\r
-       http://www.w3schools.com/tags/av_prop_error.asp\r
-       1 = MEDIA_ERR_ABORTED - fetching process aborted by user\r
-    2 = MEDIA_ERR_NETWORK - error occurred when downloading\r
-    3 = MEDIA_ERR_DECODE - error occurred when decoding\r
-    4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\r
-    */\r
-                                       error         : this[ '_rawObject' ].error || 0   // 0, 1 ~ 4 \r
-                                   };                                  \r
-                               };\r
+                                                               this[ '_rawObject' ].currentTime * 1000 | 0 );\r
+                       },\r
+               /*\r
+               http://www.w3schools.com/tags/av_prop_error.asp\r
+               1 = MEDIA_ERR_ABORTED - fetching process aborted by user\r
+           2 = MEDIA_ERR_NETWORK - error occurred when downloading\r
+           3 = MEDIA_ERR_DECODE - error occurred when decoding\r
+           4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\r
+           */                  \r
+                       getActualError : function(){\r
+                               return this[ '_rawObject' ].error || 0;\r
+                       },\r
                        \r
-                               result = X_AudioWrapper_updateStates( this, obj );\r
-                           \r
-                               if( result & 2 ){ // seek\r
-                       this.play();\r
-                               //} else\r
-                               //if( result & 1 ){\r
-                                       //if( X_Audio_HTMLAudioWrapper_currentTimeFix ){\r
-                                       //      this.play();                                            \r
-                                       //};\r
-\r
+                       afterUpdateState : function( result ){\r
+                               if( result & 3 ){ // seek\r
+                       this.actualPlay();\r
                                } else\r
                                if( result & 4 ){\r
-                      this[ '_rawObject' ].volume = this.volume;\r
-                               };\r
-                           \r
+                      this[ '_rawObject' ].volume = this.gain;\r
+                               };                              \r
                        }\r
        \r
                }\r
index f4b84ab..fd9f694 100644 (file)
@@ -18,27 +18,12 @@ var X_Audio_SLAudioWrapper,
 if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){\r
        \r
        // X.Node.inherits はできない。_rawObject は <object> でなく silverlight\r
-       X_Audio_SLAudioWrapper = X_EventDispatcher[ 'inherits' ](\r
+       X_Audio_SLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](\r
                'X.AV.SilverlightAudioWrapper',\r
                X_Class.POOL_OBJECT,\r
                {\r
-                       '_rawType'      : X_EventDispatcher_EVENT_TARGET_TYPE.SILVER_LIGHT,\r
-               proxy           : null,\r
-               \r
-                       startTime       : 0,\r
-                       endTime         : -1,\r
-                       loopStartTime   : -1,\r
-                       loopEndTime     : -1,\r
-                       seekTime        : -1,\r
-                       duration        : 0,\r
-                       \r
-                       playing         : false,\r
-                       error           : 0,                    \r
-                       loop            : false,\r
-                       looped          : false,\r
-                       autoplay        : false,\r
-                       volume          : 0.5,\r
-               \r
+                       '_rawType'      : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT,\r
+\r
                _onload         : '',\r
                _callback       : null,                 \r
                xnodeObject     : null,\r
@@ -49,13 +34,13 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                        _lastState      : '',\r
                        _interval       : 0, // setInterval timer id\r
                        \r
-                       Constructor : function( proxy, source, option ){\r
+                       Constructor : function( target, source, option ){\r
+                               var xnodeScript;\r
                                \r
                                if( !X_Audio_SLAudio_uid ){\r
                                        // source\r
-                                       // X_Node_systemNode[ 'create' ]( 'script', { type : 'text/xaml', id : 'silverlightaudio' } )\r
-                                       //      [ 'text' ]( '<Canvas xmlns="http://schemas.microsoft.com/client/2007" ' +\r
-                                       // 'xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>');\r
+                                       //xnodeScript = X_Node_head[ 'create' ]( 'script', { type : 'text/xaml', id : 'silverlightaudio' } );\r
+                                       //xnodeScript[ '_rawObject' ].innerHTML = '<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>';\r
                                        \r
                                        // html に以下を書いた                     \r
                                        // <script id="silverlightaudio" type="text/xaml"><Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas></script>\r
@@ -67,7 +52,7 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                 */\r
                                \r
                                // TODO embed\r
-                               this.proxy       = proxy;\r
+                               this.target      = target || this;\r
                                this._source     = source;\r
                                // X.Audio._slOnload_ は不可\r
                        this._onload     = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );\r
@@ -88,31 +73,29 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                            //'<param value="true" name="autoUpgrade">' +\r
                                            //'<param name="onerror" value="slerror">' // bond to global\r
                                        );\r
-                               X_AudioWrapper_updateStates( this, option );\r
+                               this.setState( option );\r
        \r
                                this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE );\r
                        },\r
                        \r
-                       onSLReady : function( sender ){\r
-                               if( !this._onload ) return;\r
-                               \r
-                               window[ this._onload ] = null;\r
-                               delete this._onload;\r
-                               X_Callback_correct( this._callback );\r
-                               delete this._callback;\r
-\r
-                               sender.children.add(\r
-                                       sender.GetHost().\r
-                                       content.\r
-                                       CreateFromXaml(\r
-                                       '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
-                                               '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.volume + '" AutoPlay="false" />' +\r
-                                       '</Canvas>'));\r
-               \r
-                               this[ '_rawObject' ] = sender.findName('media'); // x:Name='media'\r
-\r
-                               this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
-                       },\r
+                               onSLReady : function( sender ){\r
+                                       if( !this._onload ) return;\r
+                                       \r
+                                       window[ this._onload ] = null;\r
+                                       delete this._onload;\r
+                                       X_Callback_correct( this._callback );\r
+                                       delete this._callback;\r
+       \r
+                                       sender[ 'children' ][ 'add' ](\r
+                                               sender[ 'GetHost' ]()[ 'content' ][ 'CreateFromXaml' ](\r
+                                               '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
+                                                       '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.gain + '" AutoPlay="false" />' +\r
+                                               '</Canvas>'));\r
+                       \r
+                                       this[ '_rawObject' ] = sender[ 'findName' ]( 'media' ); // x:Name='media'\r
+       \r
+                                       this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
+                               },\r
                        \r
                        handleEvent : function( e ){\r
                                var lastState, currentState;\r
@@ -125,30 +108,31 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                                this.playing = false;\r
                                                this._ended  = true;\r
                                                this._paused = false;\r
-                                               this.proxy[ 'dispatch' ]( X_EVENT_ERROR ); // open failed\r
+                                               if( this.playing ){\r
+                                                       //X_Timer_once( 16, this, this.actualPlay );\r
+                                               } else {\r
+                                                       this.target[ 'dispatch' ]( X_EVENT_ERROR ); // open failed\r
+                                                       this[ 'kill' ]();                                                       \r
+                                               };\r
                                                break;\r
 \r
                                        case 'MediaOpened' :\r
                                                // http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx\r
-                                               this.duration = this[ '_rawObject' ].NaturalDuration.Seconds * 1000;\r
-                                               // TODO 'canplaythrough'\r
-                                               //this.proxy[ 'asyncDispatch' ]( 'loadstart' );\r
-                               //this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
-                               //this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
-                               //this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
-                               //this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );\r
-                               this.proxy[ 'asyncDispatch' ]( X_EVENT_READY );\r
+                                               this.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000;\r
+                               this.target[ 'asyncDispatch' ]( X_EVENT_READY );\r
                                \r
-                               this.autoplay && X_Timer_once( 16, this, this.play );\r
+                               this.autoplay && X_Timer_once( 16, this, this.actualPlay );\r
                                                break;\r
 \r
-                                       case 'MediaEnded' :                             \r
-                                               this.loop && this.playing && this.play();\r
+                                       case 'MediaEnded' :\r
+                                               //console.log( ' > ' +  this.autoLoop + ' error:' + this.error );\r
+                                               //this.autoLoop && /* this.playing && */ this.actualPlay();\r
+                                               this._ended   = true;\r
                                                break;\r
 \r
                                        case 'CurrentStateChanged' :\r
                                                lastState        = this._lastState,\r
-                                               currentState = this[ '_rawObject' ].CurrentState;\r
+                                               currentState = this[ '_rawObject' ][ 'CurrentState' ];\r
                                \r
                                                // ignore consecutive events or 'Closed' == 'Error'\r
                                                if( lastState === currentState\r
@@ -157,15 +141,17 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                                };\r
                                                this._lastState = currentState; // update last state\r
                                \r
+                                               console.log( ' > ' + currentState + ' - ' + this._lastUserAction );\r
+                               \r
                                                switch( currentState ){\r
                                                        case 'Buffering' :\r
                                                        case 'Opening' :\r
                                                                switch( this._lastUserAction ){\r
                                                                        case 'play' :\r
-                                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+                                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
                                                                                break;\r
                                                                        case 'seek' :\r
-                                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );\r
+                                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );\r
                                                                                break;\r
                                                                        case 'pause' :\r
                                                                                break;\r
@@ -182,23 +168,26 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                                                this.playing = false;\r
                                                                this._ended  = true;\r
                                                                this._paused = false;\r
-                                                               this.proxy[ 'dispatch' ]( X_EVENT_ERROR );\r
+                                                               this.target[ 'dispatch' ]( X_EVENT_ERROR );\r
+                                                               this[ 'kill' ]();\r
                                                                break;\r
 \r
                                                        // userAction.pause()              -> MediaState('Paused') -> x\r
                                                        // userAction.stop()                    -> MediaState('Paused') -> x\r
                                                        // userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended')\r
                                                        case 'Paused':\r
-                                                               this.playing = false;\r
+                                                       \r
+                                                               this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+                                                               //this.playing = false;\r
                                                                \r
                                                                switch( this._lastUserAction ){\r
                                                                        case 'play': // play() -> file end -> event('ended')\r
                                                                        case 'seek':\r
-                                                                               this.seekTime = 0;\r
+                                                                               //this.seekTime = 0;\r
                                                                                this._ended   = true;\r
                                                                                this._paused  = false;\r
-                                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
-                                                                               this._currentTime( this.startTime );\r
+                                                                               //this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+                                                                               //this.setCurrentTime( this.startTime );\r
                                                                                break;\r
                                                                        case 'pause':\r
                                                                                this._ended  = false;\r
@@ -213,23 +202,29 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                                        // media.play -> 'Playing'\r
                                                        case 'Playing':\r
                                                                this.error   = 0;\r
-                                                               this.playing = true;\r
+                                                               //this.playing = true;\r
                                                                this._ended  = false;\r
                                                                this._paused = false;\r
-                                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+                                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
                                                                break;\r
 \r
                                                        // stop()\r
                                                        case 'Stopped':\r
-                                                               this.playing = false;\r
+                                                               this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+                                                               return;\r
+                                                               \r
+                                                               //this.playing = false;\r
                                                                this._ended  = true;\r
                                                                this._paused = false;\r
-                                                               this._currentTime( this.startTime );\r
+                                                               //this.setCurrentTime( this.startTime );\r
                                                                break;\r
                                                };\r
                                                break;\r
 \r
                                        case X_EVENT_KILL_INSTANCE :\r
+                                               this.playing && this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+                                               this.playing && this.actualPause();\r
+                                       \r
                                                if( this._onload ){\r
                                                        // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応\r
                                                        window[ this._onload ] = null;\r
@@ -241,16 +236,10 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                };\r
                        },\r
                        \r
-                       close : function(){\r
-                               this.playing && this.pause();\r
-                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
-                               this[ 'kill' ]();\r
-                       },\r
-                       \r
                        // SilverlightAudio.play\r
-                       play : function(){\r
-                               var begin, end;\r
-                               \r
+                       actualPlay : function(){\r
+                               var begin, offset, end;\r
+\r
                                // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
                                if( this.error ) return;\r
                                if( !this.duration ){\r
@@ -262,27 +251,33 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                \r
                                end   = X_AudioWrapper_getEndTime( this );\r
                                begin = X_AudioWrapper_getStartTime( this, end, true ) | 0;\r
-                               \r
-                               console.log( '[SLAudio] play ' + begin + ' -> ' + end );\r
-                               \r
-                           this[ '_rawObject' ].Volume = this.volume;\r
-                           this._beginTime = begin;\r
-                           this._currentTime( begin );\r
+\r
+                               // 1 秒以下は指定できないため四捨五入\r
+                               begin = ( begin / 1000 | 0 ) * 1000 + ( 500 < begin % 1000 ? 1000 : 0 ); \r
+\r
+                           this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
                            \r
-                           if( !this.playing ){\r
+                           this.setCurrentTime( this._beginTime = begin );\r
+                           \r
+                           console.log( '[play] ' + begin + ' -> ' + end );\r
+                           \r
+                           /*\r
+                           if( offset = begin - this.getActualCurrentTime() ){\r
+                               this.setCurrentTime( begin + offset );\r
+                               console.log( ' [差補正] ' + offset + ' ct:' + this.getActualCurrentTime() + ' begin:' + begin  );\r
+                               this._beginTime = begin = this.getActualCurrentTime();\r
+                           };*/\r
+                           \r
+                           if( !this.playing || this._ended ){\r
+                               console.log( '[play] play()' + begin + ' -> ' + end );\r
                                    this[ '_rawObject' ].play();\r
-                           //this.proxy[ 'dispatch' ]( 'play' );\r
-                           \r
                            this.playing = true;\r
+                           this._ended  = false;\r
                            };\r
                    \r
                    this._timerID && X_Timer_remove( this._timerID );\r
                    \r
-                if( end < this.duration ){\r
-                                       this._timerID = X_Timer_once( end - begin, this, this._onEnded );\r
-                } else {\r
-                       delete this._timerID;\r
-                };\r
+                this._timerID = X_Timer_once( end - begin, this, this._onEnded );\r
                 \r
                                if( !this._interval ){\r
                                        this._interval = X_Timer_add( 1000, 0, this, this._onInterval );\r
@@ -294,7 +289,7 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                                delete this._interval;\r
                                                return X_Callback_UN_LISTEN;\r
                                        };\r
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
                                },\r
                                \r
                                _onEnded : function(){\r
@@ -302,103 +297,91 @@ if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){
                                        delete this._timerID;\r
                                        \r
                            if( this.playing ){\r
-                               console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this[ '_rawObject' ].Position.Seconds * 1000 | 0 ) );\r
-                               time = this[ '_rawObject' ].Position.Seconds * 1000 | 0;\r
+                               //console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) );\r
+                               time = this.getActualCurrentTime();\r
                                \r
-                               if( time <= this._beginTime ){\r
-                                       console.log( '== waiting' );\r
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
-                                       this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - this._beginTime, this, this._onEnded );\r
+                               if( time < this._beginTime ){\r
+                                       console.log( '== waiting ' + time + ' < begin:' + this._beginTime );\r
+                                       this.setCurrentTime( this._beginTime );\r
+                                       time = this.getActualCurrentTime();\r
+                                       console.log( '    > ' + time );\r
+                                       this._ended && this[ '_rawObject' ].play();\r
+                                       this._ended = false;\r
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+                                       this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - time, this, this._onEnded );\r
                                        return;\r
                                };\r
                                \r
                                time -= X_AudioWrapper_getEndTime( this );\r
                                if( time < 0 ){\r
-                                       console.log( '> onEnd ' + time );\r
+                                       console.log( ' > まだ終わらない ' + time );\r
+                                       this._ended && this[ '_rawObject' ].play();\r
+                                       this._ended = false;\r
                                        this._timerID = X_Timer_once( -time, this, this._onEnded );\r
                                        return;\r
                                };\r
                                \r
-                               if( this.loop ){\r
-                                       if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+                               if( this.autoLoop ){\r
+                                       console.log( '========= loop?' );\r
+                                       if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+                                               console.log( '========== loopした' );\r
                                                this.looped = true;\r
-                                               this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
-                                               this.play();\r
+                                               this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+                                               this.actualPlay();\r
                                        };\r
                                } else {\r
-                                       this.pause();\r
-                                       this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+                                       console.log( '========= pause' );\r
+                                       this.actualPause();\r
+                                       this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
                                };\r
                            };\r
                                },\r
                        \r
                        // SilverlightAudio.pause\r
-                       pause : function(){\r
+                       actualPause : function(){\r
                                if( this.error || !this.playing ) return;\r
                                \r
                                this._lastUserAction = 'pause';\r
-                               this.seekTime = this.state().currentTime;\r
+                               this.seekTime = this.getActualCurrentTime();\r
                                this.playing  = false;\r
                                this._paused  = true;\r
                                this._ended   = false;\r
-                                                       \r
+                               \r
                                this[ '_rawObject' ].pause();\r
-                               //this.proxy[ 'dispatch' ]( 'pause' );\r
+                               //this.target[ 'dispatch' ]( 'pause' );\r
                        },\r
-                       \r
-                       // SilverlightAudio.state\r
-                       state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
-                               var result, end;\r
-                               \r
-                               if( obj === undefined ){\r
-                                   return {\r
-                                       startTime     : this.startTime,\r
-                                       endTime       : this.endTime < 0 ? this.duration : this.endTime,\r
-                                       loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
-                                       loopEndTime   : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
-                                       \r
-                                       // 整数化 しておかないと seek 時に不具合がある。\r
-                                       currentTime   : this.playing ? this[ '_rawObject' ].Position.Seconds * 1000 | 0 : this.seekTime,\r
 \r
-                                       loop          : this.loop,\r
-                                       looped        : this.looped,\r
-                                       volume        : this.volume,\r
-                                       error         : this.error,\r
-                                       playing       : this.playing,\r
-                                       duration      : this.duration // this[ '_rawObject' ].NaturalDuration.Seconds;\r
-                                   };\r
-                               };\r
+                       getActualCurrentTime : function(){\r
+                               return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0;\r
+                       },\r
                        \r
-                               result = X_AudioWrapper_updateStates( this, obj );\r
-                           \r
-                               if( result & 2 ){ // seek\r
-                       this.play();\r
-                               } else {\r
-                                       if( result & 1 ){\r
-                                               end     = X_AudioWrapper_getEndTime( this );\r
-                                               halfway = end < this.duration;\r
-                                               this._timerID && X_Timer_remove( this._timerID );\r
-                                               \r
-                                               if( halfway ){\r
-                                                       this._timerID = X_Timer_once( end - this[ '_rawObject' ].Position.Seconds * 1000 | 0, this, this._onEnded );\r
-                                               } else {\r
-                                                       delete this._timerID;\r
-                                               };\r
-\r
-                                       };\r
-                                       if( result & 4 ){\r
-                              this[ '_rawObject' ].Volume = this.volume;\r
+                       afterUpdateState : function( result ){\r
+                               if( result & 3 ){ // seek\r
+                       this.actualPlay();\r
+                               } else\r
+                               if( result & 1 ){\r
+                                       end     = X_AudioWrapper_getEndTime( this );\r
+                                       halfway = end < this.duration;\r
+                                       this._timerID && X_Timer_remove( this._timerID );\r
+                                       \r
+                                       if( halfway ){\r
+                                               this._timerID = X_Timer_once( end - this.getActualCurrentTime(), this, this._onEnded );\r
+                                       } else {\r
+                                               delete this._timerID;\r
                                        };\r
-                               };\r
+                               } else\r
+                               if( result & 4 ){\r
+                      this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
+                               };                      \r
                        },\r
                        \r
                                // SilverlightAudio.currentTime\r
-                               _currentTime : function( time ){ // @param Number: time\r
-                                       var position = this[ '_rawObject' ].Position; // [!] create instance\r
+                               setCurrentTime : function( time ){ // @param Number: time\r
+                                       var position = this[ '_rawObject' ][ 'Position' ]; // [!] create instance\r
        \r
-                                       position.Seconds = time / 1000 | 0; // set current time\r
+                                       position[ 'Seconds' ] = time / 1000 | 0; // set current time\r
                                \r
-                                       this[ '_rawObject' ].Position = position; // [!] reattach instance\r
+                                       this[ '_rawObject' ][ 'Position' ] = position; // [!] reattach instance\r
                                }\r
 \r
                }\r
index 99d7782..45790ae 100644 (file)
@@ -27,30 +27,63 @@ var X_Audio_Sprite_shouldUse        = window.HTMLAudioElement && ( X_UA[ 'iOS' ]
                bgmLooped   : false,\r
                bgmPlaying  : false\r
        },\r
-       X_Audio_Sprite_instance;\r
+       X_Audio_Sprite_instance,\r
+       X_Audio_Sprite_numTracks,\r
+       X_Audio_Sprite_useVideo;\r
 \r
-X[ 'Audio' ][ 'Sprite' ] = {\r
-       \r
-       'shouldUse'        : X_Audio_Sprite_shouldUse,\r
+X[ 'AudioSprite' ] = function( setting ){\r
+       var tracks  = X_Audio_Sprite_TEMP.tracks,\r
+               bgms    = X_Audio_Sprite_TEMP.BGMs,\r
+               presets = X_Audio_Sprite_TEMP.presets,\r
+               urls    = setting[ 'urls' ],\r
+               video   = setting[ 'useVideo' ],\r
+               n       = video ? 1 : setting[ 'numTracks' ] || 1,\r
+               option  = {\r
+                       volume    : setting[ 'volume' ] || 0.5,\r
+                       autoplay  : false,\r
+                       startTime : 0,\r
+                       endTime   : X_Audio_Sprite_lengthSilence,\r
+                       loop      : true\r
+               },\r
+               k, i, v, track; \r
        \r
-       'needTouchFirst'   : X_Audio_Sprite_needTouchFirst,\r
+       if( X_Audio_Sprite_instance ){\r
+               X_Audio_Sprite_instance[ 'kill' ]();\r
+       } else {\r
+               X_Audio_Sprite_instance = X_Class_override( X_EventDispatcher(), X_Audio_Sprite_members );\r
+               X_ViewPort[ 'listen' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_Audio_Sprite_instance, X_Audio_Sprite_handleEvent );\r
+       };\r
        \r
-       'enableMultiTrack' : X_Audio_Sprite_enableMultiTrack,\r
+       n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks;\r
        \r
-       'create' : function( setting ){\r
-               // close()\r
-               if( X_Audio_Sprite_instance ){\r
-                       X_Audio_Sprite_instance.close();\r
-               } else {\r
-                       X_Audio_Sprite_instance = X_Class_override( X_EventDispatcher(), X_Audio_Sprite_members );\r
-                       X_ViewPort[ 'listen' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_Audio_Sprite_instance, X_Audio_Sprite_handleEvent );\r
+       for( k in setting ){\r
+               v = setting[ k ];\r
+               if( X_Type_isArray( v ) && v !== urls ){\r
+                       v = X_Object_cloneArray( v );\r
+                       for( i = v.length; i; ){\r
+                               --i;\r
+                               if( i !== 2 ) v[ i ] = X_AudioWrapper_timeStringToNumber( v[ i ] );\r
+                       };                                      \r
+                       if( v[ 2 ] ) bgms[ k ] = v;\r
+                       presets[ k ] = v;\r
                };\r
-               X_Audio_Sprite_instance.setup( setting );\r
-               return X_Audio_Sprite_instance;\r
-               \r
-       }\r
+       };\r
+       \r
+       X_Audio_startDetectionBackend( X_Audio_BACKENDS[ 0 ], X_Audio_Sprite_instance, X_Object_cloneArray( urls ), option );\r
+\r
+       X_Audio_Sprite_instance[ 'listenOnce' ]( [ X_EVENT_BACKEND_READY, X_EVENT_BACKEND_NONE ], X_AudioSprite_backendHandler );\r
+       X_Audio_Sprite_instance[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_Audio_Sprite_handleEvent );\r
+       \r
+       X_Audio_Sprite_useVideo  = video;\r
+       X_Audio_Sprite_numTracks = X_Audio_Sprite_instance[ 'numTracks' ] = n;\r
+\r
+       return X_Audio_Sprite_instance;\r
 };\r
 \r
+X[ 'AudioSprite' ][ 'shouldUse'        ] = X_Audio_Sprite_shouldUse;\r
+X[ 'AudioSprite' ][ 'needTouchFirst'   ] = X_Audio_Sprite_needTouchFirst;\r
+X[ 'AudioSprite' ][ 'enableMultiTrack' ] = X_Audio_Sprite_enableMultiTrack;\r
+\r
 // 再生が終わっているもの、終わりかけのものを探す\r
 // TODO 終わりかけのもの、と一番古いもの、どちらを再利用するか?これ以上に細かい実装を望む場合は X.Audio.Sprite は使わず自力で実装\r
 function X_Audio_Sprite_getTrackEnded(){\r
@@ -60,7 +93,7 @@ function X_Audio_Sprite_getTrackEnded(){
        \r
        for( ; i < l; ++i ){\r
                track = tracks[ i ];\r
-               state = track.state();\r
+               state = track.getState();\r
                if( !state.playing ) return track;\r
                if( track === X_Audio_Sprite_TEMP.bgmTrack ) continue;\r
                if( state.currentTime <= X_Audio_Sprite_lengthSilence + X_Audio_Sprite_lengthDistance ) return track;\r
@@ -94,84 +127,18 @@ function X_Audio_Sprite_getTrackEnded(){
 \r
 X_Audio_Sprite_members = {\r
                \r
-               setup : function( setting ){\r
-                       \r
-                       var tracks  = X_Audio_Sprite_TEMP.tracks,\r
-                               bgms    = X_Audio_Sprite_TEMP.BGMs,\r
-                               presets = X_Audio_Sprite_TEMP.presets,\r
-                               urls    = setting[ 'urls' ],\r
-                               video   = setting[ 'useVideo' ],\r
-                               n       = video ? 1 : setting[ 'numTracks' ] || 1,\r
-                               option  = {\r
-                                       volume    : setting[ 'volume' ] || 0.5,\r
-                                       autoplay  : false,\r
-                                       startTime : 0,\r
-                                       endTime   : X_Audio_Sprite_lengthSilence,\r
-                                       loop      : true\r
-                               },\r
-                               k, i, v, track;\r
-                       \r
-                       n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks;\r
-                       \r
-                       for( k in setting ){\r
-                               v = setting[ k ];\r
-                               if( X_Type_isArray( v ) && v !== urls ){\r
-                                       v = X_Object_cloneArray( v );\r
-                                       for( i = v.length; i; ){\r
-                                               --i;\r
-                                               if( i !== 2 ) v[ i ] = X_AudioWrapper_timeStringToNumber( v[ i ] );\r
-                                       };                                      \r
-                                       if( v[ 2 ] ) bgms[ k ] = v;\r
-                                       presets[ k ] = v;\r
-                               };\r
-                       };\r
-                       \r
-                       for( i = 0; i < n; ++i ){\r
-                               if( video || ( i === 1 && X_Audio_Sprite_useVideoForMulti ) ){\r
-                                       option[ 'useVideo' ] = true;\r
-                               };\r
-                               tracks.push( X[ 'Audio' ]( urls, X_Object_clone( option ) ) );\r
-                       };\r
-                       \r
-                       tracks[ n - 1 ][ 'listenOnce' ]( [ X_EVENT_BACKEND_READY, X_EVENT_BACKEND_NONE ], this, X_Audio_Sprite_handleEvent );\r
-                       \r
-                       X_Audio_Sprite_instance.numTracks = n;\r
-               },\r
+               'numTracks' : 0,\r
                \r
-               close : function(){\r
-                       var tracks  = X_Audio_Sprite_TEMP.tracks,\r
-                               bgms    = X_Audio_Sprite_TEMP.BGMs,\r
-                               presets = X_Audio_Sprite_TEMP.presets,\r
-                               k;\r
-                       \r
-                       while( tracks.length ){\r
-                               tracks.pop()[ 'kill' ]();\r
-                       };\r
-                       \r
-                       for( k in bgms ){\r
-                               delete bgms[ k ];\r
-                       };\r
-                       for( k in presets ){\r
-                               delete presets[ k ];\r
-                       };\r
-                       \r
-                       X_Audio_Sprite_TEMP.bgmTrack    = null;\r
-                       X_Audio_Sprite_TEMP.bgmPosition = 0;\r
-                       X_Audio_Sprite_TEMP.bgmName     = '';\r
-                       X_Audio_Sprite_TEMP.bgmLooped   = false;\r
-                       X_Audio_Sprite_TEMP.bgmPlaying  = false;\r
-               },\r
-               \r
-               load : function(){\r
+               'load' : function(){\r
                        var tracks = X_Audio_Sprite_TEMP.tracks,\r
                                i = 0, l = tracks.length;\r
                        for( ; i < l; ++i ){\r
                                if( X_UA[ 'WinPhone' ] ){\r
                                        console.log( 'touch -> play()' );\r
                                        //tracks[ i ].play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 );\r
-                                       this.pause( i );\r
+                                       this[ 'pause' ]( i );\r
                                } else {\r
-                                       X_Audio_getAudioWrapper( tracks[ i ] )[ '_rawObject' ].load();\r
+                                       tracks[ i ][ '_rawObject' ].load();\r
                                };\r
                        };\r
                },\r
@@ -179,7 +146,7 @@ X_Audio_Sprite_members = {
                /*\r
                 * @return uid Number\r
                 */\r
-               play : function( name ){\r
+               'play' : function( name ){\r
                        var bgm     = X_Audio_Sprite_TEMP.bgmTrack,\r
                                tracks  = X_Audio_Sprite_TEMP.tracks,\r
                                bgms    = X_Audio_Sprite_TEMP.BGMs,\r
@@ -193,9 +160,11 @@ X_Audio_Sprite_members = {
                                                // bgm変更\r
                                                X_Audio_Sprite_TEMP.bgmName     = name;\r
                                                X_Audio_Sprite_TEMP.bgmPosition = preset[ 0 ];\r
-                                               X_Audio_Sprite_TEMP.bgmPlaying  = true;\r
                                                X_Audio_Sprite_TEMP.bgmLooped   = false;\r
                                        };\r
+                                       \r
+                                       X_Audio_Sprite_TEMP.bgmPlaying = true;\r
+                                       \r
                                        if( bgm ){\r
                                                track = bgm;\r
                                        } else\r
@@ -205,22 +174,20 @@ X_Audio_Sprite_members = {
                                                track = X_Audio_Sprite_TEMP.bgmTrack = tracks[ 0 ];\r
                                        };\r
                                        \r
-                                       if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).isPlaying() ){\r
-                                               track\r
-                                                       .state( {\r
-                                                               loop          : true,\r
-                                                               looped        : X_Audio_Sprite_TEMP.bgmLooped,\r
-                                                               currentTime   : X_Audio_Sprite_TEMP.bgmPosition,\r
-                                                               startTime     : preset[ 0 ],\r
-                                                               endTime       : preset[ 1 ],\r
-                                                               loopStartTime : preset[ 3 ],\r
-                                                               loopEndTime   : preset[ 4 ]\r
-                                                       } );\r
+                                       if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).playing ){\r
+                                               track.setState({\r
+                                                               'loop'          : true,\r
+                                                               'looped'        : X_Audio_Sprite_TEMP.bgmLooped,\r
+                                                               'currentTime'   : X_Audio_Sprite_TEMP.bgmPosition,\r
+                                                               'startTime'     : preset[ 0 ],\r
+                                                               'endTime'       : preset[ 1 ],\r
+                                                               'loopStartTime' : preset[ 3 ],\r
+                                                               'loopEndTime'   : preset[ 4 ]\r
+                                                       });\r
                                        } else {\r
-                                               track\r
-                                                       .state( { looped : X_Audio_Sprite_TEMP.bgmLooped } )\r
-                                                       .play( preset[ 0 ], preset[ 1 ], true, preset[ 3 ], preset[ 4 ] )\r
-                                                       .seek( X_Audio_Sprite_TEMP.bgmPosition );\r
+                                               track.setState( { 'looped' : X_Audio_Sprite_TEMP.bgmLooped } );\r
+                                               track.play( preset[ 0 ], preset[ 1 ], true, preset[ 3 ], preset[ 4 ] );\r
+                                               track.seek( X_Audio_Sprite_TEMP.bgmPosition );\r
                                        };\r
                                        \r
                                } else {\r
@@ -228,30 +195,29 @@ X_Audio_Sprite_members = {
                                                track = X_Audio_Sprite_getTrackEnded( X_Audio_Sprite_TEMP.bgmPlaying );\r
                                                track\r
                                                        [ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent )\r
-                                                       .state( { looped : false } )\r
-                                                       .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
+                                                       .setState( { 'looped' : false } );\r
+                                               track.play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
                                        } else {\r
                                                // single track, iOS\r
                                                if( bgm ){\r
                                                        X_Audio_Sprite_TEMP.bgmPosition = bgm.currentTime();\r
-                                                       console.log( 'bgm position : ' + X_Audio_Sprite_TEMP.bgmPosition + ' isPlay:' +  bgm.isPlaying() );\r
+                                                       //console.log( 'bgm position : ' + X_Audio_Sprite_TEMP.bgmPosition + ' isPlay:' +  bgm.playing );\r
                                                        X_Audio_Sprite_TEMP.bgmTrack    = null;\r
                                                };\r
                                                track = tracks[ 0 ];\r
                                        \r
-                                               if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).isPlaying() ){\r
-                                                       track\r
-                                                               .state( {\r
-                                                                       loop          : true,\r
-                                                                       looped        : false,\r
-                                                                       startTime     : preset[ 0 ],\r
-                                                                       endTime       : preset[ 1 ],\r
-                                                                       loopStartTime : 0,\r
-                                                                       loopEndTime   : X_Audio_Sprite_lengthSilence\r
-                                                               } );\r
+                                               if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).playing ){\r
+                                                       track.setState({\r
+                                                                       'loop'          : true,\r
+                                                                       'looped'        : false,\r
+                                                                       //'currentTime'   : preset[ 0 ],\r
+                                                                       'startTime'     : preset[ 0 ],\r
+                                                                       'endTime'       : preset[ 1 ],\r
+                                                                       'loopStartTime' : 0,\r
+                                                                       'loopEndTime'   : X_Audio_Sprite_lengthSilence\r
+                                                               });\r
                                                } else {\r
-                                                       track\r
-                                                               .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );       \r
+                                                       track.play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );  \r
                                                };\r
                                        };\r
                                };\r
@@ -260,19 +226,20 @@ X_Audio_Sprite_members = {
                        return -1;\r
                },\r
                \r
-               pause : function( uid ){\r
+               'pause' : function( uid ){\r
                        var track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
                        if( X_Audio_Sprite_TEMP.bgmTrack === track ){\r
                                X_Audio_Sprite_TEMP.bgmPosition = track.currentTime();\r
                                X_Audio_Sprite_TEMP.bgmPlaying  = false;\r
                                X_Audio_Sprite_TEMP.bgmTrack    = null;\r
                        };\r
-                       track && track.play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 );\r
+                       track && track.play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence );\r
+                       track && track.seek( 0 );\r
                        this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PAUSED );\r
                        return this;\r
                },\r
                \r
-               seek : function( uid, position ){\r
+               'seek' : function( uid, position ){\r
                        var track = X_Audio_Sprite_TEMP.tracks[ uid ],\r
                                end;\r
                        if( track ){\r
@@ -283,7 +250,7 @@ X_Audio_Sprite_members = {
                        return this;\r
                },\r
                \r
-               volume : function( uid, opt_volume ){\r
+               'volume' : function( uid, opt_volume ){\r
                        var track, i;\r
                        // TODO uid = 0\r
                        if( uid === 0 ){\r
@@ -297,48 +264,64 @@ X_Audio_Sprite_members = {
                        };\r
                        track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
                        if( opt_volume === undefined ){\r
-                               return track ? track.volume() : -1;\r
+                               return track ? track.gain : -1;\r
                        };\r
                        track && track.volume( opt_volume );\r
                        return this;\r
                },\r
                \r
-               state : function( uid, opt_obj ){\r
+               'state' : function( uid, opt_obj ){\r
                        var track = X_Audio_Sprite_TEMP.tracks[ uid ],\r
                                state, start, end;\r
                        // TODO uid = 0\r
                        if( opt_obj === undefined ){\r
                                // TODO pause\r
                                if( track ){\r
-                                       state = track.state();\r
+                                       state = track.getState();\r
                                        start = state.startTime;\r
                                        return {\r
-                                       'currentTime' : state.currentTime - state.startTime,\r
-                                       'playing'     : state.startTime <= state.currentTime && state.currentTime <= state.endTime,\r
-                                       'duration'    : state.endTime - state.startTime,\r
+                                       'currentTime' : state.currentTime - start,\r
+                                       'playing'     : start <= state.currentTime && state.currentTime <= state.endTime,\r
+                                       'duration'    : state.endTime - start,\r
                                        'volume'      : X_Audio_Sprite_TEMP.volume\r
                                        };\r
                                };\r
                                return { 'volume' : X_Audio_Sprite_TEMP.volume, 'playing' : false };\r
                        };\r
-                       track && track.state( opt_obj );\r
+                       track && track.setState( opt_obj );\r
                        return this;\r
                }\r
 };\r
 \r
-function X_Audio_Sprite_handleEvent( e ){\r
-       var i, tracks, track, _e;\r
+function X_AudioSprite_backendHandler( e ){\r
+       var i, backend, option, src, name, last, _e;\r
        \r
        switch( e.type ){\r
                case X_EVENT_BACKEND_READY :\r
+               \r
+                       backend = X_Audio_BACKENDS[ e[ 'backendID' ] ];\r
+                       option  = e[ 'option' ];\r
+                       \r
+                       this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_AudioSprite_backendHandler );\r
+                       this[ 'source' ]      = src = e[ 'source' ];\r
+                       this[ 'backendName' ] = name = backend.backendName;\r
+               \r
+                       for( i = 0; i < X_Audio_Sprite_numTracks; ++i ){\r
+                               if( X_Audio_Sprite_useVideo || ( i === 1 && X_Audio_Sprite_useVideoForMulti ) ){\r
+                                       option[ 'useVideo' ] = true;\r
+                               };\r
+                               // Audiobackend の owner として null を渡すとAudioBackend 自身へ dispatch する\r
+                               X_Audio_Sprite_TEMP.tracks.push( last = backend.klass( null, e[ 'source' ], option ) );\r
+                       };\r
+\r
                        _e = {\r
                                'type'        : X_EVENT_BACKEND_READY,\r
-                               'source'      : e[ 'source' ],\r
-                               'backendName' : e[ 'backendName' ]\r
+                               'source'      : src,\r
+                               'backendName' : name\r
                        };\r
                        \r
                        if( X_Audio_Sprite_needTouchFirst ){\r
-                               if( e.backendName === 'Web Audio' ){\r
+                               if( name === 'Web Audio' ){\r
                                        _e[ 'needTouchForPlay' ] = true;\r
                                } else {\r
                                        _e[ 'needTouchForLoad' ] = true;\r
@@ -346,37 +329,46 @@ function X_Audio_Sprite_handleEvent( e ){
                        };\r
                        this[ 'asyncDispatch' ]( _e );\r
                        \r
-                       e.target\r
-                               [ 'unlisten' ]( X_EVENT_BACKEND_NONE, this, X_Audio_Sprite_handleEvent )\r
-                               [ 'listenOnce' ]( X_EVENT_READY, this, X_Audio_Sprite_handleEvent );\r
+                       last[ 'listenOnce' ]( X_EVENT_READY, this, X_AudioSprite_backendHandler );\r
 \r
                        // READY, needTouchForPlay, needTouchForLoad\r
                        if( X_Audio_HTMLAudioWrapper_durationFix ){\r
                                for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){\r
-                                       X_Audio_Sprite_instance.pause( i );\r
+                                       this[ 'pause' ]( i );\r
                                };\r
                        };\r
+                       \r
+                       return X_Callback_STOP_NOW;\r
                        break;\r
 \r
                case X_EVENT_BACKEND_NONE :\r
-                       this[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
-                       e.target[ 'unlisten' ]( X_EVENT_BACKEND_READY, this, X_Audio_Sprite_handleEvent );\r
+                       this[ 'unlisten' ]( X_EVENT_BACKEND_READY, this, X_AudioSprite_backendHandler )\r
+                               [ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+                       return X_Callback_STOP_NOW;\r
                        break;\r
                \r
                case X_EVENT_READY :\r
                        console.log( 'X.AudioSprite - Ready!' );\r
+                       \r
                        if( X_Audio_Sprite_needTouchAndroid ){\r
                                for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){\r
-                                       X_Audio_Sprite_instance.pause( i );\r
+                                       this[ 'pause' ]( i );\r
                                };\r
                                e.target[ 'listenOnce' ]( X_EVENT_MEDIA_PLAYING, this, this.asyncDispatch, [ X_EVENT_READY ] ); // Android 標準ブラウザ\r
                                return;\r
                        };\r
                        this[ 'asyncDispatch' ]( X_EVENT_READY );\r
                        break;\r
-                       \r
+       };\r
+};\r
+\r
+\r
+function X_Audio_Sprite_handleEvent( e ){\r
+       var i, tracks, track, _e, k;\r
+       \r
+       switch( e.type ){\r
                case X_EVENT_MEDIA_PLAYING :\r
-                       ( e.target === X_Audio_Sprite_TEMP.bgmTrack || !e.target.state().looped ) && this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+                       ( e.target === X_Audio_Sprite_TEMP.bgmTrack || !e.target.looped ) && this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING );\r
                        break;\r
                \r
                case X_EVENT_MEDIA_BEFORE_LOOP :\r
@@ -384,12 +376,14 @@ function X_Audio_Sprite_handleEvent( e ){
                                X_Audio_Sprite_TEMP.bgmLooped = true;\r
                                this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid\r
                        } else {\r
-                               if( e.target.state().looped ){\r
+                               if( e.target.looped ){\r
                                        //this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid\r
                                } else {\r
                                        this[ 'asyncDispatch' ]( X_EVENT_MEDIA_ENDED ); // TODO uid\r
                                };\r
                                \r
+                               console.log( '[AudioSprite] ' + X_Audio_Sprite_TEMP.bgmPlaying + ' ' + !X_Audio_Sprite_TEMP.bgmTrack );\r
+                               \r
                                // single track | iOS\r
                                if( X_Audio_Sprite_TEMP.bgmPlaying && !X_Audio_Sprite_TEMP.bgmTrack ){\r
                                        X_Audio_Sprite_TEMP.bgmTrack = e.target;\r
@@ -399,11 +393,12 @@ function X_Audio_Sprite_handleEvent( e ){
                        };\r
                        break;\r
                \r
+               // TODO Android Firefox で アクティブ検出できない!\r
                case X_EVENT_VIEW_ACTIVATE :\r
                        console.log( '■ アクティブ' );\r
                        // track.play(); or iOS need touch??\r
                        tracks = X_Audio_Sprite_TEMP.pauseTracks;\r
-                       while( tracks.length ) tracks.pop().play();\r
+                       while( tracks.length ) tracks.pop().actualPlay();\r
                        break;\r
 \r
                case X_EVENT_VIEW_DEACTIVATE :\r
@@ -413,13 +408,30 @@ function X_Audio_Sprite_handleEvent( e ){
                        i      = tracks.length;\r
                        for( ; i; ){\r
                                track = tracks[ --i ];\r
-                               track.isPlaying() && X_Audio_Sprite_TEMP.pauseTracks.push( track.pause() );\r
+                               track.playing && X_Audio_Sprite_TEMP.pauseTracks.push( track ) && track.pause();\r
                        };\r
                        break;\r
                \r
                case X_EVENT_KILL_INSTANCE :\r
+                       \r
+                       while( X_Audio_Sprite_TEMP.tracks.length ){\r
+                               X_Audio_Sprite_TEMP.tracks.pop()[ 'kill' ]();\r
+                       };\r
+                       \r
+                       for( k in X_Audio_Sprite_TEMP.bgms ){\r
+                               delete X_Audio_Sprite_TEMP.bgms[ k ];\r
+                       };\r
+                       for( k in X_Audio_Sprite_TEMP.presets ){\r
+                               delete X_Audio_Sprite_TEMP.presets[ k ];\r
+                       };\r
+                       \r
+                       X_Audio_Sprite_TEMP.bgmTrack    = null;\r
+                       X_Audio_Sprite_TEMP.bgmPosition = 0;\r
+                       X_Audio_Sprite_TEMP.bgmName     = '';\r
+                       X_Audio_Sprite_TEMP.bgmLooped   = false;\r
+                       X_Audio_Sprite_TEMP.bgmPlaying  = false;\r
+                       \r
                        X_ViewPort[ 'unlisten' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], this, X_Audio_Sprite_handleEvent );\r
-                       this.close();\r
                        break;\r
        };\r
 };\r
index 3f03b2a..f856e85 100644 (file)
@@ -16,27 +16,3 @@ X.UI = {
        \r
        currentRootData : null\r
 };\r
-\r
-/*\r
- * 'none,chrome,container' を受け取ったら、\r
- * {\r
- *     'none' : 1,\r
- *  'chrome' : 2,\r
- *  'container' : 3,\r
- *  1 : 'none',\r
- *  2 : 'chrome',\r
- *  3 : 'container'\r
- * } こんな object を返す。\r
- */\r
-function XUI_createChecker( str ){\r
-       var ret = {},\r
-               ary = str.split( ',' ),\r
-               l   = ary.length,\r
-               i   = 0, v;\r
-       for( ; i < l; ){\r
-               v = ary[ i ];\r
-               ret[ v ] = ++i;\r
-               ret[ i ] = v;\r
-       };\r
-       return ret;\r
-};\r
index 4908ed3..ff7356e 100644 (file)
@@ -117,6 +117,29 @@ XUI_attrClassProto = null,
 \r
 XUI_AttrClass = X_Class_create( 'XUI_AttrClass', X_Class.POOL_OBJECT );\r
 \r
+/*\r
+ * 'none,chrome,container' を受け取ったら、\r
+ * {\r
+ *     'none' : 1,\r
+ *  'chrome' : 2,\r
+ *  'container' : 3,\r
+ *  1 : 'none',\r
+ *  2 : 'chrome',\r
+ *  3 : 'container'\r
+ * } こんな object を返す。\r
+ */\r
+function XUI_createChecker( str ){\r
+       var ret = {},\r
+               ary = str.split( ',' ),\r
+               l   = ary.length,\r
+               i   = 0, v;\r
+       for( ; i < l; ){\r
+               v = ary[ i ];\r
+               ret[ v ] = ++i;\r
+               ret[ i ] = v;\r
+       };\r
+       return ret;\r
+};\r
 \r
        function XUI_Attr_createAttrDef( base, defs ){\r
                var F = base ? X_Object_clone( base ) : {},\r
index 3180d29..5bfc6c9 100644 (file)
@@ -1,6 +1,6 @@
 var XUI_AbstractUINode = X_EventDispatcher[ 'inherits' ](\r
        'X.UI._AbstractUINode',\r
-       X_Class.ABSTRACT | X_Class.PRIVATE_DATA,\r
+       X_Class.ABSTRACT,\r
        {\r
                phase             : 0,\r
                dirty             : XUI_Dirty.CLEAN,\r
@@ -890,10 +890,10 @@ X.UI.AbstractUINode = X_Class_create(
        X_Class.ABSTRACT,\r
        {\r
                parent : function(){\r
-                       return X_Class_getPrivate( this ).parent;\r
+                       return X_Pair_get( this ).parent;\r
                },\r
                root : function(){\r
-                       return X_Class_getPrivate( this ).root;\r
+                       return X_Pair_get( this ).root;\r
                },\r
                \r
                /*\r
@@ -901,7 +901,7 @@ X.UI.AbstractUINode = X_Class_create(
                 * サポートされていない場合は無視される.親のレイアウトによって変わる\r
                 */\r
                attr : function( nameOrObject, valueOrUnit ){\r
-                       var p = X_Class_getPrivate( this ),\r
+                       var p = X_Pair_get( this ),\r
                                layout, k, def, attrs, v;\r
                        if( nameOrObject && X_Type_isObject( nameOrObject ) ){\r
                                // setter\r
@@ -938,22 +938,22 @@ X.UI.AbstractUINode = X_Class_create(
                },\r
                \r
                listen : function( type, arg1, arg2, arg3 ){\r
-                       X_Class_getPrivate( this )[ 'listen' ]( type, arg1, arg2, arg3 );\r
+                       X_Pair_get( this )[ 'listen' ]( type, arg1, arg2, arg3 );\r
                        return this;\r
                },\r
                listenOnce : function( type, arg1, arg2, arg3 ){\r
-                       X_Class_getPrivate( this )[ 'listenOnce' ]( type, arg1, arg2, arg3 );\r
+                       X_Pair_get( this )[ 'listenOnce' ]( type, arg1, arg2, arg3 );\r
                        return this;\r
                },\r
                listening : function( type, arg1, arg2, arg3 ){\r
-                       return X_Class_getPrivate( this )[ 'listening' ]( type, arg1, arg2, arg3 );\r
+                       return X_Pair_get( this )[ 'listening' ]( type, arg1, arg2, arg3 );\r
                },\r
                unlisten : function( type, arg1, arg2, arg3 ){\r
-                       X_Class_getPrivate( this )[ 'unlisten' ]( type, arg1, arg2, arg3 );\r
+                       X_Pair_get( this )[ 'unlisten' ]( type, arg1, arg2, arg3 );\r
                        return this;\r
                },\r
                dispatch : function( e ){\r
-                       return X_Class_getPrivate( this )[ 'dispatch' ]( e );\r
+                       return X_Pair_get( this )[ 'dispatch' ]( e );\r
                },\r
                        \r
                nextNode : function(){\r
@@ -963,7 +963,7 @@ X.UI.AbstractUINode = X_Class_create(
                        \r
                },\r
                nodeIndex : function( v ){\r
-                       var data = X_Class_getPrivate( this );\r
+                       var data = X_Pair_get( this );\r
                        if( typeof v === 'number' ){\r
                                // data.nodeIndex( v );\r
                                return this;\r
@@ -975,27 +975,27 @@ X.UI.AbstractUINode = X_Class_create(
                },\r
                getX : function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).boxX;\r
+                       return X_Pair_get( this ).boxX;\r
                },\r
                getY : function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).boxY;\r
+                       return X_Pair_get( this ).boxY;\r
                },\r
                getAbsoluteX : function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).absoluteX;\r
+                       return X_Pair_get( this ).absoluteX;\r
                },\r
                getAbsoluteY: function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).absoluteY;\r
+                       return X_Pair_get( this ).absoluteY;\r
                },\r
                getWidth : function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).boxWidth;\r
+                       return X_Pair_get( this ).boxWidth;\r
                },\r
                getHeight : function(){\r
                        // dirty の場合、rootData.calculate\r
-                       return X_Class_getPrivate( this ).boxHeight;\r
+                       return X_Pair_get( this ).boxHeight;\r
                }\r
        }\r
 );\r
index 0a3ad33..1d01e5e 100644 (file)
@@ -54,7 +54,7 @@ var XUI_Layout_Canvas = X[ 'UI' ][ 'Layout' ][ 'Canvas' ] = XUI_createLayout( {
 \r
 var XUI_Box = XUI_AbstractUINode.inherits(\r
        'X.UI._Box',\r
-       X_Class.PRIVATE_DATA, // 現状 super 指定がないとconstructor未定擬時に親のconstructor が使われない\r
+       X_Class.NONE,\r
        {\r
                supportAttrs    : XUI_Attr_createAttrDef( XUI_AbstractUINode.prototype.supportAttrs, XUI_Layout_Canvas.overrideAttrsForSelf ),\r
                \r
@@ -69,7 +69,7 @@ var XUI_Box = XUI_AbstractUINode.inherits(
                scrollingX      : 0, // TODO 現在のスクロール位置\r
                scrollingY      : 0, // TODO \r
 \r
-               Constructor : function( layout, args ){\r
+               Constructor : function( user, layout, args ){\r
                        var i = 0,\r
                                l = args.length || 1,\r
                                j = -1,\r
@@ -77,10 +77,12 @@ var XUI_Box = XUI_AbstractUINode.inherits(
                        \r
                        //if( !args.length ) args = [ args ];\r
                        \r
-                       if( !this.User[ 'instanceOf' ]( X.UI.Box ) ){\r
+                       if( !user[ 'instanceOf' ]( X.UI.Box ) ){\r
                                //throw new Error( 'Box を継承したインスタンスだけが _Box のオーナーになれます' );\r
                        };                      \r
                        \r
+                       this.User   = user;\r
+                       \r
                        this.xnode  = X_Doc_create( 'div' );\r
                        \r
                        // すでに定義されていればそちらを採用\r
@@ -90,7 +92,7 @@ var XUI_Box = XUI_AbstractUINode.inherits(
                        for( ; i < l; ++i ){\r
                                arg = args[ i ];\r
                                if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.AbstractUINode ) ){\r
-                                       _data = X_Class_getPrivate( arg );\r
+                                       _data = X_Pair_get( arg );\r
                                        if( !uinodes ) this.uinodes = uinodes = [];\r
                                        uinodes[ ++j ] = _data;\r
                                        if( _data.parent ){\r
@@ -227,7 +229,7 @@ var XUI_Box = XUI_AbstractUINode.inherits(
 \r
                        //console.log( '### AddAt ' + this.phase )\r
                        for( l = _uinodes.length; i < l; ++i ){\r
-                               data = X_Class_getPrivate( _uinodes[ i ] );\r
+                               data = X_Pair_get( _uinodes[ i ] );\r
                                _p1  = p1 && data.phase < 1;\r
                                _p2  = p2 && data.phase < 2;\r
                                _p1 && data.initialize( this.root, this.rootData, this.User, this );\r
@@ -253,7 +255,7 @@ var XUI_Box = XUI_AbstractUINode.inherits(
 \r
                        //console.log( '### AddAt ' + this.phase )\r
                        for( ; i; ){\r
-                               data = X_Class_getPrivate( _uinodes[ --i ] );\r
+                               data = X_Pair_get( _uinodes[ --i ] );\r
                                if( ( n = uinodes.indexOf( data ) ) !== -1 ){\r
                                        uinodes.splice( n, 1 );\r
                                        data._remove();\r
@@ -302,32 +304,31 @@ var XUI_Box = XUI_AbstractUINode.inherits(
 X.UI.Box = X.UI.AbstractUINode.inherits(\r
        'Box',\r
        X_Class.NONE,\r
-       XUI_Box,\r
        {\r
                Constructor : function(){\r
-                       X_Class_newPrivate( this, XUI_Layout_Canvas, arguments );\r
+                       X_Pair_create( this, XUI_Box( this, XUI_Layout_Canvas, arguments ) );\r
                },\r
                \r
                add : function( node /* , node, node ... */ ){\r
-                       X_Class_getPrivate( this ).addAt( this.numNodes() + 1, Array.prototype.slice.call( arguments ) );\r
+                       X_Pair_get( this ).addAt( this.numNodes() + 1, Array.prototype.slice.call( arguments ) );\r
                        return this;\r
                },\r
                addAt : function( index, node /* , node, node ... */ ){\r
                        if( index < 0 ) index = 0;\r
-                       X_Class_getPrivate( this ).addAt( arguments[ 0 ], Array.prototype.slice.call( arguments, 1 ) );\r
+                       X_Pair_get( this ).addAt( arguments[ 0 ], Array.prototype.slice.call( arguments, 1 ) );\r
                        return this;\r
                },\r
                remove : function( node /* , node, node ... */ ){\r
-                       X_Class_getPrivate( this )[ 'remove' ]( Array.prototype.slice.call( arguments ) );\r
+                       X_Pair_get( this )[ 'remove' ]( Array.prototype.slice.call( arguments ) );\r
                        return this;\r
                },\r
                removeAt : function( from, length ){\r
-                       X_Class_getPrivate( this ).removeAt( from, length );\r
+                       X_Pair_get( this ).removeAt( from, length );\r
                        return this;\r
                },\r
                getNodesByClass : function( klass ){\r
                        var ret     = [],\r
-                               uinodes = X_Class_getPrivate( this ).uinodes,\r
+                               uinodes = X_Pair_get( this ).uinodes,\r
                                i, l, node;\r
                        if( !uinodes || uinodes.length === 0 ) return ret;\r
                        for( i = 0, l = uinodes.length; i < l; ++i ){\r
@@ -340,16 +341,16 @@ X.UI.Box = X.UI.AbstractUINode.inherits(
                        return this.getNodeAt( 0 );\r
                },\r
                getLastChild : function(){\r
-                       var uinodes = X_Class_getPrivate( this ).uinodes;\r
+                       var uinodes = X_Pair_get( this ).uinodes;\r
                        return uinodes && uinodes.length && uinodes[ uinodes.length - 1 ].User || null;\r
                },\r
                getNodeAt : function( index ){\r
                        if( index < 0 ) return null;\r
-                       var uinodes = X_Class_getPrivate( this ).uinodes;\r
+                       var uinodes = X_Pair_get( this ).uinodes;\r
                        return uinodes && uinodes[ index ].User || null;\r
                },\r
                numNodes : function(){\r
-                       var uinodes = X_Class_getPrivate( this ).uinodes;\r
+                       var uinodes = X_Pair_get( this ).uinodes;\r
                        return uinodes && uinodes.length || 0;\r
                }\r
        }\r
@@ -391,10 +392,13 @@ X.UI.Box.presets = function(){
        };\r
        \r
        if( privateKlass ){\r
+               /*\r
+                * スーパークラスの属性定義リストをレイアウトの持つ属性定義で上書きした新しい属性定義リストを作る。\r
+                */\r
                supports = XUI_Attr_createAttrDef( privateKlass.prototype.supportAttrs, layout.overrideAttrsForSelf );\r
                \r
                klass = this.inherits( privateKlass );\r
-               privateKlass.prototype.supportAttrs = supports,\r
+               privateKlass.prototype.supportAttrs = supports;\r
                privateKlass.prototype.attrClass    = XUI_Attr_preset( privateKlass.prototype.attrClass, supports, attrs );\r
        } else {\r
                supports = XUI_Attr_createAttrDef( shadow.prototype.supportAttrs, layout.overrideAttrsForSelf );\r
index d9becca..d56665b 100644 (file)
@@ -2,14 +2,17 @@ var XUI_Layout_Vertical = X[ 'UI' ][ 'Layout' ][ 'Vertical' ] = XUI_createLayout
        \r
        name : 'VerticalLayout',\r
        \r
+       /*\r
+        * \r
+        */\r
        overrideAttrsForSelf : {\r
                selectable  : false,\r
-               role        : [ 0,              XUI_Dirty.CLEAN,  XUI_Attr_USER.UINODE, XUI_Attr_Type.INIT_ONLY | XUI_Attr_Type.LIST, 'none,chrome,container' ],\r
-               width       : [ '100%',         XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
+               role        : [ 0,             XUI_Dirty.CLEAN,  XUI_Attr_USER.UINODE, XUI_Attr_Type.INIT_ONLY | XUI_Attr_Type.LIST, 'none,chrome,container' ],\r
+               width       : [ '100%',        XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
                height      : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
                childWidth  : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
                childHeight : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
-               gapY        : [ 0,              XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH ]\r
+               gapY        : [ 0,             XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH ]\r
        },\r
        \r
        overrideAttrsForChild : {\r
@@ -61,8 +64,8 @@ var XUI_Layout_Vertical = X[ 'UI' ][ 'Layout' ][ 'Vertical' ] = XUI_createLayout
                                        } else {\r
                                                w = 0;\r
                                        };\r
-                                       if( _w < w ) _w = w;                            \r
-                               };      \r
+                                       if( _w < w ) _w = w;\r
+                               };\r
                        };\r
                        _y -= gapY;\r
                } else {\r
@@ -83,17 +86,36 @@ var XUI_Layout_Vertical = X[ 'UI' ][ 'Layout' ][ 'Vertical' ] = XUI_createLayout
                \r
                if( !isNeedsDetection ){\r
                        data.boxX += x;\r
-                       data.boxY += y;                 \r
+                       data.boxY += y;\r
                };\r
                return !ret;\r
        }\r
 });\r
 \r
-X.UI.VBox = X.UI.Box.presets(\r
-       XUI_Layout_Vertical,\r
+var XUI_VBox;\r
+\r
+X.UI.VBox = X.UI.Box.inherits(\r
        'VBox',\r
+       X_Class.NONE,\r
        {\r
-               gapY       : '0.2em',\r
-               childWidth : '100%'\r
-       }\r
-);\r
+               Constructor : function(){\r
+                       var supports;\r
+                       \r
+                       if( !XUI_VBox ){\r
+                               supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Vertical.overrideAttrsForSelf );\r
+                               \r
+                               XUI_VBox = XUI_Box.inherits(\r
+                                                       {\r
+                                                               layout       : XUI_Layout_Vertical,\r
+                                                               supportAttrs : supports,\r
+                                                               attrClass    : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+                                                                               gapY       : '0.2em',\r
+                                                                               childWidth : '100%'\r
+                                                                       } )\r
+                                                       }\r
+                                               );\r
+                       };\r
+                       X_Pair_create( this, XUI_VBox( this, XUI_Layout_Vertical, arguments ) );\r
+               }\r
+       });\r
+\r
index 1e74391..1efff7a 100644 (file)
@@ -86,10 +86,37 @@ var XUI_Layout_Horizontal = X[ 'UI' ][ 'Layout' ][ 'Horizontal' ] = XUI_createLa
        }\r
 });\r
 \r
+/*\r
 X.UI.HBox = X.UI.Box.presets(\r
        XUI_Layout_Horizontal,\r
        'HBox',\r
        {\r
                gapX : '0.2em'\r
        }\r
-);\r
+);*/\r
+\r
+var XUI_HBox;\r
+\r
+X.UI.HBox = X.UI.Box.inherits(\r
+       'HBox',\r
+       X_Class.NONE,\r
+       {\r
+               Constructor : function(){\r
+                       var supports;\r
+                       \r
+                       if( !XUI_HBox ){\r
+                               supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Horizontal.overrideAttrsForSelf );\r
+                               \r
+                               XUI_HBox = XUI_Box.inherits(\r
+                                                       {\r
+                                                               layout       : XUI_Layout_Horizontal,\r
+                                                               supportAttrs : supports,\r
+                                                               attrClass    : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+                                                                               gapX       : '0.2em'\r
+                                                                       } )\r
+                                                       }\r
+                                               );      \r
+                       };\r
+                       X_Pair_create( this, XUI_HBox( this, XUI_Layout_Horizontal, arguments ) );\r
+               }\r
+       });
\ No newline at end of file
index f0efbaa..06c2a6d 100644 (file)
@@ -78,6 +78,7 @@ var XUI_Layout_Tile = X[ 'UI' ][ 'Layout' ][ 'Tile' ] = XUI_createLayout( {
        }\r
 });\r
 \r
+/*\r
 X.UI.TileBox = X.UI.Box.presets(\r
        'TileBox',\r
        XUI_Layout_Tile,\r
@@ -87,4 +88,31 @@ X.UI.TileBox = X.UI.Box.presets(
                hCenter : true,\r
                vCenter : true\r
        }\r
-);\r
+);*/\r
+\r
+X.UI.TileBox = X.UI.Box.inherits(\r
+       'TileBox',\r
+       X_Class.NONE,\r
+       {\r
+               Constructor : function(){\r
+                       var supports;\r
+                       \r
+                       if( !XUI_TileBox ){\r
+                               supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Tile.overrideAttrsForSelf );\r
+                               \r
+                               XUI_TileBox = XUI_Box.inherits(\r
+                                                       {\r
+                                                               layout       : XUI_Layout_Tile,\r
+                                                               supportAttrs : supports,\r
+                                                               attrClass    : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+                                                                               gapX    : '0.2em',\r
+                                                                               gapY    : '0.2em',\r
+                                                                               hCenter : true,\r
+                                                                               vCenter : true\r
+                                                                       })\r
+                                                       }\r
+                                               );      \r
+                       };\r
+                       X_Pair_create( this, XUI_TileBox( this, XUI_Layout_Tile, arguments ) );\r
+               }\r
+       });
\ No newline at end of file
index 5935ebd..82b387b 100644 (file)
@@ -1,14 +1,14 @@
 var XUI_ChromeBox = XUI_Box.inherits(\r
        '_ChromeBox',\r
-       X_Class.PRIVATE_DATA,\r
+       X_Class.NONE,\r
        {\r
                chromeNodes   : null,\r
                containerNode : null,\r
                \r
-               Constructor : function( layout, args ){\r
+               Constructor : function( user, layout, args ){\r
                        var uinodes, i, l, node, after, index = 0;\r
                        \r
-                       this[ 'Super' ]( layout, args );\r
+                       this[ 'Super' ]( user, layout, args );\r
 \r
                        uinodes = this.uinodes;\r
                        l = i   = uinodes.length;\r
@@ -19,7 +19,7 @@ var XUI_ChromeBox = XUI_Box.inherits(
                                        if( this.containerNode ){\r
                                                //throw new Error( 'ContainerNode が複数設定されています!ContainerNode はクロームボックスにひとつ、生成時に設定できます ' + node );\r
                                        };\r
-                                       this.containerNode = node.User;\r
+                                       this.containerNode  = node.User;\r
                                        this._containerNode = node;\r
                                } else {\r
                                        if( !this.chromeNodes ) this.chromeNodes = [];\r
@@ -47,55 +47,54 @@ var XUI_ChromeBox = XUI_Box.inherits(
 X.UI.ChromeBox = X.UI.Box.inherits(\r
        'ChromeBox',\r
        X_Class.NONE,\r
-       XUI_ChromeBox,\r
        {\r
                Constructor : function(){\r
-                       X_Class_newPrivate( this, XUI_Layout_Canvas, arguments );\r
+                       X_Pair_create( this, XUI_ChromeBox( this, XUI_Layout_Canvas, arguments ) );\r
                },\r
                add : function( node /* , node, node ... */ ){\r
-                       X_Class_getPrivate( this ).containerNode.addAt( this.numNodes(), Array.prototype.slice.call( arguments ) );\r
+                       X_Pair_get( this ).containerNode.addAt( this.numNodes(), Array.prototype.slice.call( arguments ) );\r
                        return this;\r
                },\r
                addAt : function( index, node /* , node, node ... */ ){\r
-                       X_Class_getPrivate( this ).containerNode.addAt( index, Array.prototype.slice.call( arguments, 1 ) );\r
+                       X_Pair_get( this ).containerNode.addAt( index, Array.prototype.slice.call( arguments, 1 ) );\r
                        return this;\r
                },\r
                remove : function( node /* , node, node ... */ ){\r
-                       X_Class_getPrivate( this ).containerNode[ 'remove' ]( arguments );\r
+                       X_Pair_get( this ).containerNode[ 'remove' ]( arguments );\r
                        return this;\r
                },\r
                removeAt : function( from, length ){\r
-                       X_Class_getPrivate( this ).containerNode.removeAt( from, length );\r
+                       X_Pair_get( this ).containerNode.removeAt( from, length );\r
                        return this;\r
                },\r
                getNodesByClass : function( klass ){\r
-                       return X_Class_getPrivate( this ).containerNode.User.getNodesByClass( klass );\r
+                       return X_Pair_get( this ).containerNode.User.getNodesByClass( klass );\r
                },\r
                getFirstChild : function(){\r
-                       return X_Class_getPrivate( this ).containerNode.User.getFirstChild();\r
+                       return X_Pair_get( this ).containerNode.User.getFirstChild();\r
                },\r
                getLastChild : function(){\r
-                       return X_Class_getPrivate( this ).containerNode.User.getLastChild();\r
+                       return X_Pair_get( this ).containerNode.User.getLastChild();\r
                },\r
                getNodeByUID : function( uid ){\r
-                       return X_Class_getPrivate( this ).containerNode.User.getNodeByUID();\r
+                       return X_Pair_get( this ).containerNode.User.getNodeByUID();\r
                },\r
                getNodeAt : function( index ){\r
-                       return X_Class_getPrivate( this ).containerNode.User.getNodeAt( index );\r
+                       return X_Pair_get( this ).containerNode.User.getNodeAt( index );\r
                },\r
                numNodes : function(){\r
-                       return X_Class_getPrivate( this ).containerNode.User.numNodes();\r
+                       return X_Pair_get( this ).containerNode.User.numNodes();\r
                },\r
                getContainerNode : function(){\r
-                       return X_Class_getPrivate( this ).containerNode.User;\r
+                       return X_Pair_get( this ).containerNode.User;\r
                },\r
                getChromeNodeAt : function( index ){\r
                        if( index < 0 ) return null;\r
-                       var nodes = X_Class_getPrivate( this ).chromeNodes;\r
+                       var nodes = X_Pair_get( this ).chromeNodes;\r
                        return nodes ? nodes[ index ].User || null : null;\r
                },\r
                numChromeNodes : function(){\r
-                       var nodes = X_Class_getPrivate( this ).chromeNodes;\r
+                       var nodes = X_Pair_get( this ).chromeNodes;\r
                        return nodes ? nodes.length : 0;\r
                }\r
        }\r
index be6884f..1fe9486 100644 (file)
@@ -44,7 +44,7 @@ var X_UI_ScrollBox_SUPPORT_ATTRS = {
 \r
 var XUI_ScrollBox = XUI_ChromeBox.inherits(\r
        '_ScrollBox',\r
-       X_Class.PRIVATE_DATA,\r
+       X_Class.NONE,\r
        {\r
                directionLockThreshold : 10,\r
                scrollXEnabled         : true,\r
@@ -104,9 +104,9 @@ var XUI_ScrollBox = XUI_ChromeBox.inherits(
                _containerNode   : null,\r
                xnodeSlider      : null,\r
                \r
-               Constructor : function( layout, args ){\r
-                       this[ 'Super' ]( layout, args );\r
-                       this._containerNode = X_Class_getPrivate( this.containerNode );\r
+               Constructor : function( user, layout, args ){\r
+                       this[ 'Super' ]( user, layout, args );\r
+                       this._containerNode = X_Pair_get( this.containerNode );\r
                        this.xnodeSlider = this._containerNode.xnode[ 'className' ]( 'ScrollSlider' ).listen( X_EVENT_ANIME_END, this, X_UI_ScrollBox_onAnimeEnd );\r
                        this.xnode[ 'className' ]( 'ScrollBox' );\r
                },\r
@@ -503,26 +503,26 @@ function X_UI_ScrollBox_onAnimeEnd( e ){
        };\r
        if( !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){\r
                this.isInTransition = false;\r
-               this.dispatch( XUI_Event.SCROLL_END );\r
+               this[ 'dispatch' ]( XUI_Event.SCROLL_END );\r
        };\r
        return X_Callback_NONE;\r
 };\r
 \r
+// TODO Box の継承に!\r
 X.UI.ScrollBox = X.UI.ChromeBox.inherits(\r
        'ScrollBox',\r
        X_Class.NONE,\r
-       XUI_ScrollBox,\r
        {\r
                Constructor : function(){\r
                        var args = [\r
-                                               XUI_Layout_Vertical,                    \r
+                                               XUI_Layout_Canvas,                      \r
                                                {\r
-                                                       name   : 'ScrollBox-Scroller',\r
-                                                       role   : 'container',\r
-                                                       width       : 'auto',\r
-                                                       minWidth    : '100%',\r
-                                                       height      : 'auto',\r
-                                                       minHeight   : '100%'\r
+                                                       name      : 'ScrollBox-Scroller',\r
+                                                       role      : 'container',\r
+                                                       width     : 'auto',\r
+                                                       minWidth  : '100%',\r
+                                                       height    : 'auto',\r
+                                                       minHeight : '100%'\r
                                                }\r
                                        ],\r
                                i    = arguments.length,\r
@@ -541,16 +541,19 @@ X.UI.ScrollBox = X.UI.ChromeBox.inherits(
                                };\r
                        };\r
                        \r
-                       X_Class_newPrivate(\r
+                       X_Pair_create(\r
                                this,\r
-                               XUI_Layout_Canvas,\r
-                               [\r
-                                       {\r
-                                               width  : '100%',\r
-                                               height : '100%'\r
-                                       },\r
-                                       X.UI.VBox.apply( 0, args )\r
-                               ]\r
+                               XUI_ScrollBox(\r
+                                       this,\r
+                                       XUI_Layout_Canvas,\r
+                                       [\r
+                                               {\r
+                                                       width  : '100%',\r
+                                                       height : '100%'\r
+                                               },\r
+                                               X.UI.VBox.apply( 0, args )\r
+                                       ]\r
+                               )\r
                        );\r
                        \r
                        attr && this.attr( attr );\r
index 86913ba..b589337 100644 (file)
@@ -1,14 +1,15 @@
 var XUI_Text = XUI_AbstractUINode.inherits(\r
        '_Text',\r
-       X_Class.PRIVATE_DATA,\r
+       X_Class.NONE,\r
        {\r
                content     : null,\r
                \r
-               Constructor : function( content ){\r
-                       if( !( this.User[ 'instanceOf' ]( X.UI.Text ) ) ){\r
+               Constructor : function( user, content ){\r
+                       if( !( user[ 'instanceOf' ]( X.UI.Text ) ) ){\r
                                alert( 'Text を継承したインスタンスだけが _Text のオーナーになれます' );\r
                        };\r
-                       this.xnode      = X_Doc_create( 'div' );\r
+                       this.User  = user;\r
+                       this.xnode = X_Doc_create( 'div' );\r
                        \r
                        if( X_Type_isString( content ) && content ){\r
                                this.content = content;\r
@@ -22,14 +23,14 @@ var XUI_Text = XUI_AbstractUINode.inherits(
 X.UI.Text = X.UI.AbstractUINode.inherits(\r
        'Text',\r
        X_Class.NONE,\r
-       XUI_Text,\r
        {\r
                Constructor : function( opt_content, opt_attrObj ){\r
-                       X_Class_newPrivate( this, opt_content );\r
+                       X_Pair_create( this, XUI_Text( this, opt_content ) );\r
+\r
                        X_Type_isObject( opt_attrObj = opt_attrObj || opt_content ) && this[ 'attr' ]( opt_attrObj );\r
                },\r
                content : function( v ){\r
-                       var data = X_Class_getPrivate( this );\r
+                       var data = X_Pair_get( this );\r
                        if( data.content !== v ){\r
                                data.xnode && data.xnode[ 'text' ]( v );\r
                                data.rootData.reserveCalc();\r
index c67f5b0..fdcec3a 100644 (file)
@@ -3,7 +3,7 @@ var X_UI_rootData  = null,
 
 function X_UI_eventRellay( e ){
        var font    = X_ViewPort_baseFontSize,
-               x       = e.pageX / font, // clientX iOS4- で通らない?
+               x       = e.pageX / font, // clientX は iOS4- で通らない?
                y       = e.pageY / font,
                type    = XUI_Event.NameToID[ e.type ],
                i       = 0,
@@ -69,7 +69,7 @@ function X_UI_eventRellay( e ){
 
 var XUI_PageRoot = XUI_Box.inherits(
        '_PageRoot',
-       X_Class.FINAL | X_Class.PRIVATE_DATA,
+       X_Class.FINAL,
        {
                layout                : XUI_Layout_Canvas,
                
@@ -82,8 +82,8 @@ var XUI_PageRoot = XUI_Box.inherits(
                eventCounter          : null,
                cursorStyle           : null,
                
-               Constructor : function( layout, args ){
-                       this[ 'Super' ]( layout, args );
+               Constructor : function( user, layout, args ){
+                       this[ 'Super' ]( user, layout, args );
                        
                        if( X_ViewPort_readyState === X_EVENT_XDOM_READY ){
                                X_Timer_once( 0, this, this.start );
@@ -179,6 +179,29 @@ function XUI_PageRoot_onViewUpdate( e ){
        this[ 'dispatch' ]( XUI_Event.LAYOUT_COMPLETE );
 };
 
+//var XUI_PageRoot;
+
+X.UI.PageRoot = X.UI.Box.inherits(
+       'PageRoot',
+       X_Class.NONE,
+       {
+               Constructor : function(){
+                       var supports;
+                       
+                       //if( !XUI_PageRoot ){
+                               supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Canvas.overrideAttrsForSelf );
+                               
+                               XUI_PageRoot.prototype.layout       = XUI_Layout_Canvas;
+                               XUI_PageRoot.prototype.supportAttrs = supports;
+                               XUI_PageRoot.prototype.attrClass    = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {
+                                                                       width  : '100%',
+                                                                       height : '100%'
+                                                               } );
+                       //};
+                       X_Pair_create( this, XUI_PageRoot( this, XUI_Layout_Canvas, arguments ) );
+               }
+       });
+/*
 X.UI.PageRoot = X.UI.Box.presets(
        'PageRoot',
        XUI_PageRoot,
@@ -186,5 +209,5 @@ X.UI.PageRoot = X.UI.Box.presets(
                width  : '100%',
                height : '100%'
        }
-);
+);*/