OSDN Git Service

Version 0.6.112, fix X.Node.Anime, rewite X.Audio.* for AudioSprite, add X.Audio...
authoritozyun <itozyun@user.sourceforge.jp>
Tue, 11 Nov 2014 07:29:33 +0000 (16:29 +0900)
committeritozyun <itozyun@user.sourceforge.jp>
Tue, 11 Nov 2014 07:29:33 +0000 (16:29 +0900)
14 files changed:
0.6.x/js/01_core/02_XUA.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/14_XTimer.js
0.6.x/js/01_core/16_XViewPort.js
0.6.x/js/02_dom/02_XNode.js
0.6.x/js/02_dom/03_XDomEvent.js
0.6.x/js/02_dom/10_XNodeAnime.js
0.6.x/js/03_plugin/00_XPlugin.js
0.6.x/js/06_net/00_XNet.js
0.6.x/js/06_net/01_XNetXHR.js
0.6.x/js/07_audio/00_XAudio.js
0.6.x/js/07_audio/02_XHTMLAudio.js
0.6.x/js/07_audio/03_XSilverlightAudio.js

index e84f289..bd6353e 100644 (file)
@@ -16,6 +16,35 @@ var X_UA = (function( n, undefined ){
        console.log( ' platform   : ' + n.platform );\r
        console.log( '-' );\r
        \r
+       if( n.platform.indexOf( 'Win' ) + 1 ){\r
+               console.log( 'Win' );\r
+               acme.Windows = true;\r
+               if( n.platform === 'Win16' ) acme.Win16 = true;\r
+               if( n.platform === 'Win32' ) acme.Win32 = true;\r
+               if( n.platform === 'Win64' ) acme.Win64 = true;\r
+               if( n.platform === 'WinCE' ) acme.WinCE = true;\r
+       } else\r
+       if( n.platform.indexOf( 'Mac' ) + 1 ){\r
+               console.log( 'Mac' );\r
+               acme.Mac = true;\r
+               if( n.platform === 'MacPPC' || n.platform === 'MacPowerPC' ) acme.MacPPC = true;\r
+               if( n.platform === 'Mac68K' ) acme.Mac68K = true;\r
+               if( n.platform === 'MacIntel' ) acme.MacIntel = true;\r
+       } else\r
+       if( n.platform.indexOf( 'Linux' ) + 1 ){\r
+               console.log( 'Linux' );\r
+               acme.Linux = true;\r
+       };\r
+\r
+/*\r
+ * http://bizmakoto.jp/bizid/articles/1207/31/news004.html\r
+Chrome         Android 4.0以上       Google\r
+Dolphin Browser HD     Android 2.0.1以上     Mobotap\r
+Firefox        Android 2.2以上       Mozilla\r
+Opera Mobile   Android 1.6以上       Opera Software ASA\r
+Sleipnir Mobile        Android 2.1以上       Fenrir\r
+ */\r
+       \r
        if( window.opera ){\r
                i = dua.indexOf( 'Opera' ); // Opera/\r
                j = dua.indexOf( 'Version/' );\r
@@ -56,7 +85,7 @@ var X_UA = (function( n, undefined ){
                acme.IE8      = 8 <= acme.IE && acme.IE < 9;\r
                acme.IE9      = 9 <= acme.IE && acme.IE < 10;\r
                acme.MacIE    = dua.indexOf( 'Mac_PowerPC' ) !== -1 || dua.indexOf( 'Mac_PPC' ) !== -1 || dua.indexOf( 'Mac_68K' ) !== -1;\r
-               acme.IEMobile = dua.toLowerCase().indexOf( 'iemobile' ) !== -1 || n.platform === 'WinCE';\r
+               acme.IEMobile = dua.toLowerCase().indexOf( 'iemobile' ) !== -1 || acme.WinCE;\r
                acme.WinPhone = dua.toLowerCase().indexOf( 'windows phone' ) !== -1;\r
                console.log( '>> IE : ' + acme.IE + ' ActiveX : ' + acme.ActiveX );\r
                // TODO XBox360, XBox1, Modern or Desktop, Standalone\r
@@ -70,7 +99,7 @@ var X_UA = (function( n, undefined ){
                return acme;\r
        };\r
        \r
-       if( n.platform === 'Linux' && tv === 2 && dua.indexOf( 'Sony\/COM2\/' ) !== -1 ){\r
+       if( acme.Linux && tv === 2 && dua.indexOf( 'Sony\/COM2\/' ) !== -1 ){\r
                acme.NetFront = 3.4;\r
                console.log( '>> NetFront : ' + acme.NetFront );\r
                return acme;\r
index 8dc4e17..1bcb3f9 100644 (file)
@@ -80,8 +80,8 @@ X.Event = {
 // in_page_jump\r
 // on_screen_keyboard_show\r
 // on_screen_keyboard_hide\r
-       BEFORE_UPDATE          : 21,// このイベントで要素のサイズを取得すると無限ループに!\r
-       UPDATED                : 22,\r
+       BEFORE_UPDATE          : 21,// X_System このイベントで要素のサイズを取得すると無限ループに!\r
+       UPDATED                : 22,// X_System\r
        AFTER_UPDATE           : 23,\r
 \r
        HASH_CHANGED           : 24,\r
@@ -95,7 +95,7 @@ X.Event = {
        BACKEND_CHANGED        : 30\r
 };\r
 \r
-X_Event_last = 29;\r
+X_Event_last = 30;\r
 \r
 X_TEMP.onSystemReady.push(\r
        function(){\r
index 1ae7244..594701c 100644 (file)
@@ -76,7 +76,7 @@ X.EventDispatcher =
                 * _rawObject には HTMLElement, window, document, XHR といったイベントターゲットオブジェクトを設定します。\r
                 * _rawObject が設定されていると listen(), unlisten() 時に addEventListener(DOM Level2) や detachEvent(ie5~8), on~(DOM0) 等を操作します。\r
                 * _rawObject は最初の listen() 前に設定しておかないと addEventListener 等が意図したように行われません。\r
-                * X.Node では非同期に HTMLElement を生成していて、要素生成以前に on, off を呼び出すことができます。これは適宜に migrateEvent, restoreEvent を呼んで解決しているためです。\r
+                * X.Node では非同期に HTMLElement を生成していて、要素生成以前に listen, unlisten を呼び出すことができます。これは適宜に X_EventDispatcher_toggleAllEvents を呼んで解決しているためです。\r
                 * @private\r
                 * @type {Object}\r
                 */\r
@@ -565,7 +565,7 @@ var X_EventDispatcher_actualAddEvent =
  * animation も怪しい、、、\r
  */\r
 function X_EventDispatcher_iOSTransitionEndDispatch( e ){\r
-       return X_Node_getXNode( this ).dispatch( X_Event_RenameTo[ e.type ] );\r
+       return X_Node_getXNode( this ).dispatch( X_Event_RenameTo[ e.type ] || e.type );\r
 };\r
 \r
 /*\r
index 242f18e..2ee2757 100644 (file)
@@ -44,7 +44,7 @@ var X_Timer_now = Date.now || function(){ return +new Date; },
        X_Timer_REQ_ANIME_FRAME =\r
                window.requestAnimationFrame ||\r
                window.webkitRequestAnimationFrame ||\r
-               window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame ||\r
+               window.mozRequestAnimationFrame ||\r
                window.oRequestAnimationFrame ||\r
                window.msRequestAnimationFrame ||\r
                false,\r
@@ -160,7 +160,8 @@ X.Timer = {
                                        if( ( f = list[ --i ] ).uid < uid ) break;\r
                                        if( f.uid === uid ){\r
                                                list.splice( i, 1 );\r
-                                               l === 1 && X_Timer_CANCEL_ANIME_FRAME( X_Timer_requestID );\r
+                                               // gecko では無い場合がある\r
+                                               l === 1 && X_Timer_CANCEL_ANIME_FRAME && X_Timer_CANCEL_ANIME_FRAME( X_Timer_requestID );\r
                                                break;\r
                                        };\r
                                };                              \r
index 61c898f..6b655ce 100644 (file)
@@ -53,6 +53,9 @@ X_ViewPort = X_Class_override(
                                case 'visibilitychange' :
                                        X_ViewPort.asyncDispatch( ( X_ViewPort_active = document[ 'hidden' ] ) ? X.Event.VIEW_DEACTIVATE : X.Event.VIEW_ACTIVATE );
                                        break;
+                               case 'mozvisibilitychange' :
+                                       X_ViewPort.asyncDispatch( ( X_ViewPort_active = document[ 'mozHidden' ] ) ? X.Event.VIEW_DEACTIVATE : X.Event.VIEW_ACTIVATE );
+                                       break;
                                case 'webkitvisibilitychange' :
                                        X_ViewPort.asyncDispatch( ( X_ViewPort_active = document[ 'webkitHidden' ] ) ? X.Event.VIEW_DEACTIVATE : X.Event.VIEW_ACTIVATE );
                                        break;
@@ -372,13 +375,16 @@ X.ViewPort = {
 //ブラウザの戻るボタンで戻ったときに呼ばれるイベントとかキャッシュとかそこらへんのこと
 //http://d.hatena.ne.jp/koumiya/20080916/1221580149
 
-                       if( document[ 'hidden' ] !== undefined ) {// iOS 7+
+                       if( document[ 'hidden' ] !== undefined ){// iOS 7+
                                X_EventDispatcher_systemListen( X_ViewPort_document, 'visibilitychange', X_ViewPort );
                        } else
-                       if( document[ 'webkitHidden' ] !== undefined ) {
+                       if( document[ 'mozHidden' ] !== undefined ){
+                               X_EventDispatcher_systemListen( X_ViewPort_document, 'mozvisibilitychange', X_ViewPort );
+                       } else
+                       if( document[ 'webkitHidden' ] !== undefined ){
                                X_EventDispatcher_systemListen( X_ViewPort_document, 'webkitvisibilitychange', X_ViewPort );
                        } else
-                       if( X_UA.iOS && window[ 'onpageshow' ] !== undefined ) {
+                       if( X_UA.iOS && window[ 'onpageshow' ] !== undefined ){
                                X_EventDispatcher_systemListen( X_ViewPort, [ 'pageshow', 'pagehide' ] );
                        } else {
                                X_EventDispatcher_systemListen( X_ViewPort, [ 'focus', 'blur' ] );
index 525eb81..dc4cb55 100644 (file)
@@ -965,7 +965,7 @@ function X_Node_reserveUpdate(){
        if( !X_Node_updateTimerID ) X_Node_updateTimerID = X.Timer.requestFrame( X_Node_startUpdate );\r
 };\r
 \r
-function X_Node_startUpdate(){\r
+function X_Node_startUpdate( time ){\r
        var removal, i, xnode, tmp;\r
        \r
        if( !X_Node_updateTimerID || X_ViewPort_readyState < X_TEMP.SYSTEM_EVENT_INIT ){\r
@@ -975,9 +975,10 @@ function X_Node_startUpdate(){
        X.Timer.cancelFrame( X_Node_updateTimerID );\r
        X_Node_updateTimerID = 0;\r
 \r
-\r
-       // このイベントでサイズを取ると無限ループに\r
-       X_System._listeners && X_System._listeners[ X.Event.BEFORE_UPDATE ] && X_System.dispatch( X.Event.BEFORE_UPDATE );\r
+       if( time ){\r
+               // X.Timer 経由でないと発火しない このイベントでサイズを取ると無限ループに\r
+               X_System._listeners && X_System._listeners[ X.Event.BEFORE_UPDATE ] && X_System.dispatch( X.Event.BEFORE_UPDATE );\r
+       };\r
 \r
        removal = X_Node_reserveRemoval;\r
        \r
@@ -1001,7 +1002,10 @@ function X_Node_startUpdate(){
        \r
        //console.log( 'end of _startUpdate().' );\r
        \r
-       X_System._listeners && X_System._listeners[ X.Event.UPDATED ] && X_System.dispatch( X.Event.UPDATED );\r
+       if( time ){\r
+               // X.Timer 経由でないと発火しない このイベントでサイズを取ると無限ループに\r
+               X_System._listeners && X_System._listeners[ X.Event.UPDATED ] && X_System.dispatch( X.Event.UPDATED );  \r
+       };\r
        \r
        X_ViewPort._listeners && X_ViewPort._listeners[ X.Event.AFTER_UPDATE ] && X_ViewPort.asyncDispatch( X.Event.AFTER_UPDATE );\r
        //this._rawObject.style.visibility = tmp;\r
@@ -1362,7 +1366,7 @@ var X_Node__afterActualCreate =
                        X_Node__afterActualCreate( xnodes[ i ] );\r
                };\r
                // src の onload があるので先ではないか?\r
-               // ie の str から要素を作る場合、srcだけ イベント設定後ではないか?\r
+               // TODO ie の str から要素を作る場合、srcだけ イベント設定後ではないか?\r
                X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰\r
        }) :\r
        X_UA_DOM.IE4 ? (function( that ){\r
index 3b1c59e..0700cec 100644 (file)
@@ -21,7 +21,7 @@ var X_Dom_Event_devicePixelRatio = window.devicePixelRatio || ( window.screen.de
 if( !X_UA.IE || 9 <= X_UA.IE ){\r
        X.Dom.Event = function( e, xnode ){\r
                var originalType = e.type,\r
-                       type, pointerType,\r
+                       type, pointerEventType,\r
                        touches, events,\r
                        altKey, ctrlKey, metaKey, shiftKey, target, related, force,\r
                        elm, i, n, time, touch, ev;\r
@@ -36,6 +36,7 @@ if( !X_UA.IE || 9 <= X_UA.IE ){
                                this.source = e.source;\r
                                break;\r
                        case 'progress' :\r
+                               this.lengthComputable = e.lengthComputable;\r
                                this.loaded = e.loaded;\r
                                this.total  = e.total;\r
                                break;\r
@@ -98,17 +99,17 @@ if( !X_UA.IE || 9 <= X_UA.IE ){
                        this.offsetX       = e.offsetX;\r
                        this.offsetY       = e.offsetY;\r
                } else\r
-               if( pointerType = X_Event_toPointer[ originalType ] ){\r
+               if( pointerEventType = X_Event_toPointer[ originalType ] ){\r
                        // Touch or Mouse\r
-                       //console.log( originalType + ' => ' + pointerType );\r
+                       //console.log( originalType + ' => ' + pointerEventType );\r
                        \r
                        /* e.constructor === window.TouchEvent -> e.touches for iOS3.13 */\r
                        if( touches = e.changedTouches ){\r
-                               //console.log( originalType + ' => ' + pointerType );\r
+                               //console.log( originalType + ' => ' + pointerEventType );\r
                                if( touches.length === 0 ){\r
                                        alert( 'e.changedTouches.length === 0' );\r
                                };\r
-                               xnode._cancelMouse = pointerType;\r
+                               xnode._cancelMouse = pointerEventType;\r
                                \r
                                events   = [];\r
                                altKey   = e.altKey;\r
@@ -122,7 +123,7 @@ if( !X_UA.IE || 9 <= X_UA.IE ){
                                        target  = touch.target;\r
                                        related = touch.relatedTarget;\r
                                        events[ i ] = {\r
-                                               type          : pointerType,\r
+                                               type          : pointerEventType,\r
                                                pointerType   : 'touch',\r
                                                target        : X_Node_getXNode( target.nodeType === 3 ? target.parentNode : target ),// defeat Safari bug // xnodetouch.target,\r
                                                currentTarget : xnode,\r
@@ -156,14 +157,14 @@ if( !X_UA.IE || 9 <= X_UA.IE ){
                                return events.length === 1 ? events[ 0 ] : events;\r
                        } else {\r
                                \r
-                               if( xnode._cancelMouse === pointerType ){\r
+                               if( xnode._cancelMouse === pointerEventType ){\r
                                        delete xnode._cancelMouse;\r
                                        console.log( '**** xnode._cancelMouse ' + xnode._cancelMouse );\r
                                        return [];\r
                                };\r
                                \r
                                // MouseEvent;\r
-                               this.type          = type;\r
+                               this.type          = pointerEventType;\r
                                this.pointerType   = 'mouse';\r
                                \r
                                this.button        = e.button !== undefined ? e.button :\r
@@ -228,7 +229,7 @@ if( !X_UA.IE || 9 <= X_UA.IE ){
                                if( e.deltaY !== undefined ){\r
                                        this.deltaX = e.deltaX;\r
                                        this.deltaY = e.deltaY;\r
-                                       this.deltaZ = e.deltaZ;\r
+                                       this.deltaZ = e.deltaZ || 0;\r
                                } else\r
                                if( e.wheelDeltaY !== undefined ){\r
                                        this.deltaX = e.wheelDeltaX / 120;\r
index ec0dac9..2adfebb 100644 (file)
@@ -59,6 +59,7 @@ var
 // アニメ中の remove\r
 \r
 var X_Node_ANIMATIONS            = [],\r
+       X_Node_Anime_reserved        = false,\r
        X_Node_Anime_updateTimerID   = 0,\r
        X_Node_Anime_needsDetection  = false,\r
        X_Node_Anime_hasTransform    = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],\r
@@ -67,24 +68,25 @@ var X_Node_ANIMATIONS            = [],
        X_Node_Anime_hasTransition   = !!X_Node_CSS_VENDER_PREFIX[ 'transitionDelay' ] && !X.UA.Opera, // Opera12(XP,8.1) 切った方がスムース\r
        X_Node_Anime_transitionProps = X_Node_Anime_hasTransform ? X_Node_CSS_VENDER_PREFIX[ 'transform' ] : 'left,top';\r
 \r
+// gpu化だけ transformX , willChange\r
+// 終了位置の変更\r
+// 中断\r
+\r
+\r
 Node.prototype.animate = function( start, dest, duration, easing, wait ){\r
-       var obj = this._anime || ( this._anime = {} ), current;\r
-       \r
-       if( X_Node_Anime_hasTransition && this._rawObject ){\r
-               current = {}; //X_Node_Anime_getComputedPosition( this );\r
-       };\r
+       var obj = this._anime || ( this._anime = {} );\r
        \r
-       obj.duration  = X.Type.isFinite( duration ) && 0 <= duration ? duration : 500;\r
+       obj.duration  = 0 <= duration && X.Type.isFinite( duration ) ? duration : 500;\r
        obj.easing    = ease[ easing ] || ease.circular;\r
        // 現在 GPUレイヤーのtop になっているか?将来については phase で判定\r
        obj.gpuParent = obj.gpuParent || false;\r
-       obj.phase     = duration === 0 ? 9 : 0; //\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 || current && current.x || 0;\r
-       obj.startY    = ( start.y || start.y === 0 ) ? start.y : obj.y || current && current.y || 0;\r
-       obj.startA    = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : obj.a || current && current.a || 1;\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.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
@@ -99,7 +101,7 @@ Node.prototype.animate = function( start, dest, duration, easing, wait ){
        \r
        X_Node_Anime_needsDetection = true;\r
        if( X_Node_Anime_hasTransition ){\r
-               X_Node_Anime_reserveUpdate();\r
+               X_Node_Anime_reserveUpdate( true );\r
        } else {\r
                X_Node_Anime_updateTimerID || ( X_Node_Anime_updateTimerID = X.Timer.requestFrame( X_Node_Anime_updateAnimationsNoTransition ) );               \r
        };\r
@@ -127,19 +129,29 @@ Node.prototype.stop = function(){
        return this;\r
 };\r
 \r
-function X_Node_Anime_reserveUpdate(){\r
-       if( !X_Node_Anime_updateTimerID ){\r
-               // Opera12 requestAnimationFrame では transition が動かない、、、\r
-               X_Node_Anime_updateTimerID =\r
-                       X_UA.Opera ?\r
-                               X.Timer.once( 0, X_Node_Anime_updateAnimations ) :\r
-                               X.Timer.requestFrame( X_Node_Anime_updateAnimations );\r
+function X_Node_Anime_reserveUpdate( before ){\r
+       if( !X_Node_Anime_reserved ){\r
+               X_Node_Anime_reserved = true;\r
+               \r
+               if( X_Node_updateTimerID ){\r
+                       console.log( before ? '> BEFORE_UPDATE' : '> UPDATED' );\r
+                       X_System.listenOnce( before ? X.Event.BEFORE_UPDATE : X.Event.UPDATED, X_Node_Anime_updateAnimations );\r
+               } else {\r
+                       console.log( '> Timer' );\r
+                       // Opera12 requestAnimationFrame では transition が動かない、、、\r
+                       X_Node_Anime_updateTimerID =\r
+                               X_UA.Opera ?\r
+                                       X.Timer.once( 0, X_Node_Anime_updateAnimations ) :\r
+                                       X.Timer.requestFrame( X_Node_Anime_updateAnimations );                  \r
+               };\r
        };\r
 };\r
 \r
-function X_Node_Anime_updateAnimations(){\r
+function X_Node_Anime_updateAnimations( v ){\r
        var i = X_Node_ANIMATIONS.length, ret, c = false;\r
        \r
+       console.log( v.type || v );\r
+       \r
        //console.log( 'updateAnimations ' + i + ' ' + X_Node_Anime_needsDetection );\r
        \r
        if( X_Node_Anime_needsDetection ) X_Node_Anime_detectAnimationLayers();\r
@@ -158,7 +170,10 @@ function X_Node_Anime_updateAnimations(){
                };\r
        };\r
        \r
+       if( X_Node_Anime_updateTimerID && X_Node_updateTimerID ) X_Node_startUpdate();\r
+       \r
        X_Node_Anime_updateTimerID = 0;\r
+       X_Node_Anime_reserved = false;\r
        if( c ){\r
                X_Node_Anime_reserveUpdate();\r
        };\r
@@ -168,43 +183,41 @@ function X_Node_Anime_updateAnimations(){
 function X_Node_Anime_detectAnimationLayers(){\r
        var i = X_Node_ANIMATIONS.length,\r
                l = i,\r
-               j, xnode, parent, hasGPUChild, changed, remove;\r
+               j, xnode, parent, hasGPUChild, remove;\r
 \r
        for( ; i; ){\r
                xnode = X_Node_ANIMATIONS[ --i ];\r
                parent = hasGPUChild = false;\r
-               //console.log( 'koko- ' + xnode._id + ' ' + xnode._anime.phase );\r
                for( j = l; j; ){\r
                        _xnode = X_Node_ANIMATIONS[ --j ];\r
                        \r
                        if( xnode.parent === _xnode.parent ){\r
-                               //console.log( 'cont ' + xnode._anime.phase );\r
                                continue;\r
                        } else\r
                        if( _xnode.contains( xnode ) ){\r
-                               if( _xnode._anime.phase === 3 || _xnode._anime.phase === 10 ){\r
+                               if( ( _xnode._anime.phase === 3 && _xnode._anime.gpuParent ) || _xnode._anime.phase === 10 ){\r
                                        _xnode._anime.phase = 15;\r
                                } else\r
                                if( xnode._anime.gpuParent ){\r
-                                       changed = parent = true;\r
+                                       parent = true;\r
                                        xnode._anime.phase = xnode._anime.phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄\r
                                } else\r
                                if( [ 7, 8, 9, 13, 14 ].indexOf( xnode._anime.phase ) !== -1 ){// GPU レイヤーの中止\r
-                                       changed = parent = true;\r
+                                       parent = true;\r
                                        xnode._anime.phase -= 8;\r
                                };\r
                                break;\r
                        } else\r
                        if( xnode.contains( _xnode ) ){\r
-                               if( xnode._anime.phase === 3 || xnode._anime.phase === 10 ){\r
+                               if( ( xnode._anime.phase === 3 && xnode._anime.gpuParent ) || xnode._anime.phase === 10 ){\r
                                        xnode._anime.phase = 15;\r
                                } else\r
                                if( _xnode._anime.gpuParent ){\r
-                                       changed = hasGPUChild = true;\r
+                                       hasGPUChild = true;\r
                                        _xnode._anime.phase = _xnode._anime.phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄\r
                                } else\r
                                if( [ 7, 8, 9, 13, 14 ].indexOf( _xnode._anime.phase ) !== -1 ){// GPU レイヤーの中止\r
-                                       changed = hasGPUChild = true;\r
+                                       hasGPUChild = true;\r
                                        _xnode._anime.phase -= 8;\r
                                };\r
                                break;\r
@@ -213,13 +226,10 @@ function X_Node_Anime_detectAnimationLayers(){
                if( !parent && xnode._anime.phase !== 15 ){\r
                        if( xnode._anime.phase === 0 ){\r
                                // 新規\r
-                               changed = changed || !xnode._anime.gpuParent;\r
                                xnode._anime.phase = hasGPUChild ? 7 : 8;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
                        } else\r
                        if( [ 3, 4, 10, 100 ].indexOf( xnode._anime.phase ) === -1 ){\r
                                // 非GPU -> GPU\r
-                               changed = changed || !xnode._anime.gpuParent;\r
-                               //console.log( 'koko? ' + xnode._anime.phase );\r
                                xnode._anime.phase = hasGPUChild ? 13 : 14;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。\r
                        };\r
                };\r
@@ -239,13 +249,14 @@ function X_Node_Anime_updateAnimation( xnode ){
                        break;\r
                case  0 : // 開始位置+アニメーションの設定 \r
                case  8 :\r
+                       X_Node_Anime_readyTransition( xnode );\r
                        X_Node_Anime_updatePosition( xnode, obj.startX, obj.startY, obj.startA, phase === 8 );\r
                        ++obj.phase;\r
                        break;\r
                case  1 :\r
                case  9 : // 終了位置の設定\r
                        obj.gpuParent = phase === 9;\r
-                       X_Node_Anime_updateTransition( xnode, obj.duration, obj.easing );\r
+                       obj.duration ? X_Node_Anime_updateTransition( xnode ) : X_Node_Anime_clearTransition( xnode );\r
                        X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, obj.gpuParent );\r
                        obj.phase = 2;\r
                        break;\r
@@ -255,7 +266,7 @@ function X_Node_Anime_updateAnimation( xnode ){
                        return false;\r
                \r
                case 3 : // アニメーションの解除\r
-                       X_Node_Anime_updateTransition( xnode, 0 );\r
+                       X_Node_Anime_clearTransition( xnode );\r
                        obj.phase = obj.gpuParent ? 10 : 4;\r
                        break;\r
 \r
@@ -283,7 +294,7 @@ function X_Node_Anime_updateAnimation( xnode ){
                        now  = X_Timer_now();\r
                        time = obj.duration - now + obj.startTime;\r
                        if( time < 16 ){\r
-                               X_Node_Anime_updateTransition( xnode, 0 );\r
+                               X_Node_Anime_clearTransition( xnode );\r
                                X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, phase === 14 );\r
                                obj.phase = phase === 14 ? 10 : 4;\r
                        } else {\r
@@ -293,7 +304,6 @@ function X_Node_Anime_updateAnimation( xnode ){
                                obj.startA    = current.a;\r
                                obj.duration  = time;\r
                                obj.startTime = now;\r
-                               //X_Node_Anime_updateTransition( xnode, time, obj.easing );\r
                                X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, phase === 14 );\r
                                obj.phase = phase === 14 ? 9 : 1;\r
                        };\r
@@ -302,7 +312,7 @@ function X_Node_Anime_updateAnimation( xnode ){
                case 15 :\r
                        // GPU有効で停止(待機)している xnode の解除\r
                        console.log( 'GPU有効で停止(待機)している xnode の解除' + xnode._tag + xnode.getOrder() );\r
-                       X_Node_Anime_updateTransition( xnode, 0 );\r
+                       X_Node_Anime_clearTransition( xnode );\r
                        X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );\r
                        obj.gpuTimerID && X.Timer.remove( obj.gpuTimerID );\r
                        return true;\r
@@ -311,7 +321,7 @@ function X_Node_Anime_updateAnimation( xnode ){
                        console.log( 'stop() gpu:' + obj.gpuParent );\r
                        current = X_Node_Anime_getComputedPosition( xnode );\r
                                                \r
-                       X_Node_Anime_updateTransition( xnode, 0 );\r
+                       X_Node_Anime_clearTransition( xnode );\r
                        X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, obj.gpuParent );\r
                        obj.phase = obj.gpuParent ? 10 : 4;\r
                        break;\r
@@ -341,7 +351,7 @@ function X_Node_Anime_onTransitionEnd(){
        X_Node_Anime_needsDetection = true;\r
        X_Node_Anime_reserveUpdate();\r
        console.log( 'トランジション終了' );\r
-       return X.Callback.UN_LISTEN | X.Callback.PREVENT_DEFAULT;\r
+       return X.Callback.UN_LISTEN | X.Callback.PREVENT_DEFAULT | X.Callback.STOP_PROPAGATION;\r
 };\r
 \r
 function X_Node_Anime_releaseGPULayer(){\r
@@ -353,22 +363,39 @@ function X_Node_Anime_releaseGPULayer(){
        console.log( 'GPUレイヤーの破棄' );\r
 };\r
 \r
-function X_Node_Anime_updateTransition( xnode, time, easing ){\r
+function X_Node_Anime_readyTransition( xnode ){\r
+       xnode.css({\r
+               willChange               : X_Node_Anime_transitionProps + ',opacity,width,height',\r
+               backfaceVisibility       : 'hidden',\r
+               transitionTimingFunction : xnode._anime.easing.style,\r
+               transitionDelay          : '0s'\r
+       });\r
+};\r
+\r
+function X_Node_Anime_updateTransition( xnode ){\r
        // 開始座標のセット(新規のみ)\r
        // アニメーション指定のセット(または解除)(対象のみ)\r
        // 目標座標のセット\r
-       if( time ){\r
-               xnode.listenOnce( 'transitionend', X_Node_Anime_onTransitionEnd );\r
-       } else {\r
-               xnode.unlisten( 'transitionend', X_Node_Anime_onTransitionEnd );\r
-       };\r
+       xnode.listenOnce( 'transitionend', X_Node_Anime_onTransitionEnd );\r
+\r
+       xnode.css({\r
+               transitionProperty       : X_Node_Anime_transitionProps + ',opacity,width,height',\r
+               transitionDuration       : xnode._anime.duration + 'ms'\r
+       });\r
+};\r
+\r
+function X_Node_Anime_clearTransition( xnode ){\r
+       // 開始座標のセット(新規のみ)\r
+       // アニメーション指定のセット(または解除)(対象のみ)\r
+       // 目標座標のセット\r
+       xnode.unlisten( 'transitionend', X_Node_Anime_onTransitionEnd );\r
+\r
        xnode.css({\r
-               transitionProperty       : X_Node_Anime_transitionProps + ',opacity',\r
-               willChange               : time ? X_Node_Anime_transitionProps + ',opacity' : '',\r
-               backfaceVisibility       : time ? 'hidden' : '',\r
-               transitionTimingFunction : time ? easing.style : '',\r
-               transitionDelay          : '0s',\r
-               transitionDuration       : time ? time + 'ms' : ''\r
+               willChange               : '',\r
+               backfaceVisibility       : '',\r
+               transitionTimingFunction : '',\r
+               transitionDelay          : '',\r
+               transitionDuration       : ''\r
        });\r
 };\r
 \r
index 73bdf2f..f3868f8 100644 (file)
@@ -9,12 +9,18 @@
  * flash player 10, ie6+, ff2+, safari3, opera9.5
  * flash player 11, ie7+, ff4(?), safari5, opera11
  * flash player 12, ie8+, ff17, opera11
+ * 
+ * http://helpx.adobe.com/jp/flash-player/kb/228683.html
+ * flash player  9, Mac 10.1-10.3, 98, ME
+ * flash player 10.1, Mac 10.4
+ * flash player 10.3, Mac 10.5
+ * flash player 11.1, Win2k, Android 2.x-4.x
  */
 var X_Pulgin_FLASH_VERSION =
                !X_UA.IE && navigator.plugins[ 'Shockwave Flash' ] ?
                        parseFloat( navigator.plugins[ 'Shockwave Flash' ].version ) :
                !X_UA.IE4 && !X_UA.IE5 && X_UA.ActiveX ? (function(){
-                           var obj = eval( 'var a;try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}a' ),
+                           var obj = eval( 'var a,e;try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}a' ),
                                ver = obj && obj.GetVariable( '$version' ).split( ' ' ).join( '.' );
                                return parseFloat( ver ) || 0;
                        })() :
@@ -48,7 +54,7 @@ var X_Pulgin_FLASH_VERSION =
                !X_UA.IE && navigator.plugins[ 'Unity Player' ] ?
                        parseFloat( navigator.plugins[ 'Unity Player' ].version ) :
                !X_UA.IE4 && !X_UA.IE5 && X_UA.ActiveX ? (function(){
-                           var obj = eval( 'var a;try{a=new ActiveXObject("UnityWebPlayer.UnityWebPlayer.1")}catch(e){}a' );
+                           var obj = eval( 'var a,e;try{a=new ActiveXObject("UnityWebPlayer.UnityWebPlayer.1")}catch(e){}a' );
                            return obj ? parseFloat( obj.GetPluginVersion() ) : 0;
                        })() :
                        0,
@@ -57,20 +63,36 @@ var X_Pulgin_FLASH_VERSION =
                X_UA.ActiveX ? !!X_Pulgin_UNITY_VERSION :
                        navigator.mimeTypes &&
                        navigator.mimeTypes[ 'application/vnd.unity' ] &&
-                       navigator.mimeTypes[ 'application/vnd.unity' ].enabledPlugin;
+                       navigator.mimeTypes[ 'application/vnd.unity' ].enabledPlugin,
+
+       X_Pulgin_GEARS_ENABLED =
+               window.GearsFactory ||
+               ( X_UA.ActiveX && 6 <= X_UA.IE ?
+                       (function(){
+                           return eval( 'var a,e;try{a=new ActiveXObject("Gears.Factory")}catch(e){}!!a' );
+                       })() :
+                       navigator.mimeTypes &&
+                       navigator.mimeTypes[ 'application/x-googlegears' ] &&
+                       navigator.mimeTypes[ 'application/x-googlegears' ].enabledPlugin
+               );
 
 X.Pulgin = {
        
        Flash              : X_Pulgin_FLASH_VERSION,
 
-       FlashEnabled       : X_Pulgin_FLASH_ENABLED,
+       FlashEnabled       : !!X_Pulgin_FLASH_ENABLED,
 
        Silverlight        : X_Pulgin_SILVER_LIGHT_VERSION,
        
-       SilverlightEnabled : X_Pulgin_SILVER_LIGHT_ENABLED,
+       SilverlightEnabled : !!X_Pulgin_SILVER_LIGHT_ENABLED,
        
        Unity              : X_Pulgin_UNITY_VERSION,
        
-       UnityEnabled       : X_Pulgin_UNITY_ENABLED
+       UnityEnabled       : !!X_Pulgin_UNITY_ENABLED,
+       
+       GearsEnabled       : X_Pulgin_GEARS_ENABLED
        
 };
+
+if( X_Pulgin_GEARS_ENABLED ) alert( 'X_Pulgin_GEARS_ENABLED' );
+
index 096ad2f..d85ea3b 100644 (file)
@@ -2,8 +2,8 @@
 // local への通信に対しては、netspeed を更新しない\r
 X.Net = {\r
 \r
-       xhrGet : function( url ){\r
-               return new X_NET_Queue( X_NET_TYPE_XHR, { method : 'GET', url : url } );\r
+       xhrGet : function( url, type ){\r
+               return new X_NET_Queue( X_NET_TYPE_XHR, { method : 'GET', url : url, type : type } );\r
        },\r
        \r
        xhrPost : function( url, postbody ){\r
index 11e5ba7..fa2e49c 100644 (file)
@@ -86,6 +86,7 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                        _busy      : false,\r
                        _canceled  : false,\r
                        _percent   : 0,\r
+                       _timerID   : 0,\r
                        \r
                        load : function( obj ){\r
                                var raw      = this._rawObject,\r
@@ -96,7 +97,7 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                                        password = obj[ 'password' ],\r
                                        headers  = obj[ 'headers' ],\r
                                        postbody = obj[ 'postbody' ],\r
-                                       timeout  = obj[ 'timeout' ],\r
+                                       timeout  = obj[ 'timeout' ] || 20000,\r
                                        temp;\r
                                \r
                                if( obj[ 'type' ] ){\r
@@ -127,8 +128,30 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                                                };\r
                                        };\r
                                };\r
-                               //if( raw.timeout !== undefined ) raw.timeout = timeout || -1;\r
-                               raw.open( method, url, false );\r
+                               \r
+                               raw.open( method, url, true );\r
+                               \r
+                               if( raw.responseType !== undefined ){\r
+                                       switch( this._type ){\r
+                                               case '' :\r
+                                               case 'text' :\r
+                                                       raw.responseType = 'text';\r
+                                                       break;\r
+                                               case 'json' :\r
+                                               case 'moz-json' :\r
+                                                       break;\r
+                                               case 'document' :\r
+                                               case 'xml' :\r
+                                               case 'html' :\r
+                                               case 'htm' :\r
+                                                       raw.responseType = 'document';\r
+                                                       break;\r
+                                               case 'blob' :\r
+                                               case 'arraybuffer' :\r
+                                                       raw.responseType = this._type;\r
+                                                       break;\r
+                                       };\r
+                               };\r
                                \r
                                // http://www.quirksmode.org/blog/archives/2005/09/xmlhttp_notes_r_1.html\r
                                // raw.overrideMimeType()\r
@@ -138,6 +161,12 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                                        };              \r
                                };\r
                                \r
+                               if( raw.timeout !== undefined ){\r
+                                       raw.timeout = timeout; //Firefox33 でエラー,,,\r
+                               } else {\r
+                                       this._timerID = X.Timer.once( timeout, this, this.onTimeout );\r
+                               };      \r
+                               \r
                                // send 前にフラグを立てる,回線が早いと raw.send() 内で onload -> _busy = false ののち、 _busy = true するため。\r
                                this._busy = true;\r
                                \r
@@ -148,14 +177,33 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                        },\r
                        \r
                        cancel : function(){\r
-                               X.Net.XHR.CANCELABLE && this._rawObject.abort();\r
+                               /* X.Net.XHR.CANCELABLE && */ this._rawObject.abort && this._rawObject.abort();\r
                                this._canceled = true;\r
+                               this.asyncDispatch( X.Event.CANCELED );\r
                        },\r
                        \r
                        reset : function(){\r
+                               // XMLHttpRequest で順番にリソースを取得する\r
+                               // http://note.chiebukuro.yahoo.co.jp/detail/n16248\r
+                               // TODO Opera 10.10 と Safari 4.1 はエラーが起きた XHR を再利用できないので毎回作る\r
+                               \r
+                               // \r
+                               // domes.lingua.heliohost.org/dom-intro/load-save2.html\r
+                               // 規定上は open() を呼び出すと XMLHttpRequest オブジェクトが未送信状態に戻りますが、\r
+                               // Opera 10.10、Safari 4.1 では、同一オリジン制限に違反した XMLHttpRequest オブジェクトは再度 open() しても未送信状態に戻りません。\r
+                               if( X_UA.Opera || X_UA.Webkit ){\r
+                                       \r
+                               };\r
+\r
+                               // XMLHttpRequest の使い方\r
+                               // http://webos-goodies.jp/archives/50548720.html\r
+                               // XMLHttpRequest オブジェクトを再利用する際も、 abort メソッドを呼び出す必要があるようです。\r
+                               this._rawObject.abort && this._rawObject.abort();\r
+\r
                                this._method   = this._type = '';\r
                                this._canceled = this._busy = false;\r
-                               this._percent  = 0;\r
+                               this._timerID && X.Timer.remove( this._timerID );\r
+                               this._percent  = this._timerID = 0;\r
                        },\r
                        \r
                        handleEvent : function( e ){\r
@@ -253,7 +301,6 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                                                break;\r
                                        \r
                                        case 'progress' :\r
-                                               // TODO X.Dom.Event でコピーしていないのでまだ動かない、、、\r
                                                if( e.lengthComputable ){\r
                                                        this._percent = e.loaded / e.total;\r
                                                        live && this.asyncDispatch( { type : X.Event.PROGRESS, percent : this._percent } );\r
@@ -265,18 +312,25 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
                                                this._busy = false;\r
                                                live && this.asyncDispatch( { type : X.Event.ERROR, status : raw.status } );\r
                                                break;\r
-                                       \r
-                                       //case 'abort' :\r
-                                       //      this._busy = false;\r
-                                       //      this.asyncDispatch( { type : X.Event.ERROR, status : raw.status } );\r
-                                       //      break;\r
+\r
                                        case 'timeout' : // Gecko 12.0 https://developer.mozilla.org/ja/docs/XMLHttpRequest/Synchronous_and_Asynchronous_Requests\r
                                                this._busy = false;\r
-                                               live && this.asyncDispatch( { type : X.Event.ERROR, status : raw.status } );\r
+                                               this.asyncDispatch( X.Event.TIMEOUT );\r
                                                break;\r
                                };\r
                        },\r
                        \r
+                       onTimeout : function(){\r
+                               var raw  = this._rawObject,\r
+                                       live = !X_NET_XHRWrapper._canceled || !this._busy;\r
+\r
+                               if( raw.readyState < 3 ){\r
+                                       this._busy = false;\r
+                                       live && this.asyncDispatch( X.Event.TIMEOUT );\r
+                               };\r
+                               this._timerID = 0;\r
+                       },\r
+                       \r
                        onUploadProgress : X.Net.XHR.UL_PROGRESS && function( e ){\r
                                var raw  = X_NET_XHRWrapper._rawObject.upload,\r
                                        live = !X_NET_XHRWrapper._canceled,\r
@@ -287,25 +341,23 @@ if( X_Net_XHR_W3C || X_Net_XHR_ACTIVE_X ){
        );\r
        // 同期リクエストでなければならない場合, unload, beforeunload時\r
 \r
-\r
-       // ie8 では timeout が有効, MSXML のバージョンは関係なさそう、、、\r
        if( X_UA.IE8 ){\r
-               X_NET_XHRWrapper.listen( [ 'readystatechange', 'error', 'abort', 'timeout' ] );\r
+               X_NET_XHRWrapper.listen( [ 'readystatechange', 'error', 'timeout' ] ); //, 'abort'\r
        } else\r
        if( X_UA.IE7 ){\r
                if( X_URL_IS_LOCAL ){\r
                        X_NET_XHRWrapper.listen( 'readystatechange' ); // ie7 ActiveX の場合、error は不可\r
                } else {\r
-                       X_NET_XHRWrapper.listen( [ 'readystatechange', 'error' ] ); // ie7 ActiveX の場合、error は不可\r
+                       X_NET_XHRWrapper.listen( [ 'readystatechange', 'error' ] );\r
                };\r
        } else\r
        if( X_Net_XHR_ACTIVE_X ){ // win ie5-6\r
                X_NET_XHRWrapper.listen( 'readystatechange' );\r
        } else \r
        if( X.Net.XHR.PROGRESS ){\r
-               X_NET_XHRWrapper.listen( [ 'load', 'progress', 'error', 'abort', 'timeout' ] );\r
+               X_NET_XHRWrapper.listen( [ 'load', 'progress', 'error', 'timeout' ] ); //, 'abort'\r
        } else {\r
-               X_NET_XHRWrapper.listen( [ 'load', 'readystatechange', 'error', 'abort', 'timeout' ] );\r
+               X_NET_XHRWrapper.listen( [ 'load', 'readystatechange', 'error', 'timeout' ] ); //, 'abort'\r
        };\r
        \r
        if( X.Net.XHR.UL_PROGRESS ){\r
index 4675b13..59a6be7 100644 (file)
@@ -1,12 +1,13 @@
 \r
 X.Audio = {\r
-       HTML5       : 1,\r
-       Flash       : 2,\r
-       Silverlight : 3,\r
-       Unity       : 4,\r
-       WMP         : 5,\r
-       RealPlayer  : 6,\r
-       QuickTime   : 7,\r
+       WebAudio    : 1,\r
+       HTML5       : 2,\r
+       Flash       : 3,\r
+       Silverlight : 4,\r
+       Unity       : 5,\r
+       WMP         : 6,\r
+       RealPlayer  : 7,\r
+       QuickTime   : 8,\r
        \r
        create : function( sourceList, opt_option ){\r
                return new X_AudioProxy( X.Type.isArray( sourceList ) ? X_Object_cloneArray( sourceList ) : [ sourceList ], opt_option || {} );\r
@@ -20,40 +21,42 @@ var X_Audio_BACKENDS = [];
  */\r
 \r
 function X_Audio_detectBackend( proxy, sourceList, option ){\r
-       var source = sourceList.shift() || '', \r
-               parts  = source.split( '?' )[ 0 ].split( '#' )[ 0 ].split( '.' ),\r
-               ext    = parts[ parts.length - 1 ],\r
-               backend, ext, sup;\r
+       var source  = sourceList.shift() || '', \r
+               parts   = source.split( '?' )[ 0 ].split( '#' )[ 0 ].split( '.' ),\r
+               ext     = parts[ parts.length - 1 ],\r
+               backend = X_Audio_BACKENDS[ 0 ],\r
+               ext, sup;\r
        \r
-       if( source && X_Audio_BACKENDS.length ){\r
+       if( source && backend ){\r
                sup      = [ proxy, option, sourceList, source, ext ];\r
                sup[ 5 ] = sup;\r
-               X_Audio_BACKENDS[ 0 ]\r
-                       .detect( source, ext )\r
-                       .listenOnce( [ 'support', 'nosupport' ], X_Audio_detectComplete, sup );\r
+               \r
+               proxy.listenOnce( [ 'support', 'nosupport' ], backend, X_Audio_detectComplete, sup );\r
+               backend.detect( proxy, source, ext );   \r
        } else {\r
-               proxy.asyncDispatch( 0, 'nobackend' );\r
+               proxy.asyncDispatch( 'nobackend' );\r
        };\r
 };\r
 \r
 function X_Audio_detectComplete( e, proxy, option, sourceList, source, ext, sup ){\r
        var i = X_Audio_BACKENDS.indexOf( this ), backend;\r
        \r
-       this.unlisten( [ 'support', 'nosupport' ], X_Audio_detectComplete, sup );\r
+       proxy.unlisten( [ 'support', 'nosupport' ], backend, X_Audio_detectComplete, sup );\r
        \r
        switch( e.type ){\r
                case 'support' :\r
                        proxy._backend = i;\r
-                       proxy.asyncDispatch( 0, { type : 'backendfound', option : option, source : source } );\r
+                       proxy.asyncDispatch( { type : 'backendfound', option : option, source : source } );\r
                        break;\r
                case 'nosupport' :\r
                        if( backend = X_Audio_BACKENDS[ i + 1 ] ){\r
-                               backend.detect( source, ext ).listen( [ 'support', 'nosupport' ], X_Audio_detectComplete, sup );\r
+                               proxy.listenOnce( [ 'support', 'nosupport' ], backend, X_Audio_detectComplete, sup );\r
+                               backend.detect( proxy, source, ext );\r
                        } else\r
                        if( sourceList.length ){\r
                                X_Audio_detectBackend( proxy, sourceList, option );\r
                        } else {\r
-                               proxy.asyncDispatch( 0, 'nobackend' );\r
+                               proxy.asyncDispatch( 'nobackend' );\r
                        };\r
                        break;\r
        };\r
@@ -74,44 +77,91 @@ var X_AudioProxy = X.EventDispatcher.inherits(
                },\r
                \r
                close : function(){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].close.call( this );\r
+                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].close( this );\r
                },\r
                \r
-               play : function( position ){\r
-                       //console.log( 'proxy play ' + this._backend );\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].play.call( this, position );\r
+               play : function( startTime, endTime, loop, loopStartTime ){\r
+                       var state, duration;\r
+                       if( startTime ){\r
+                               this.state( {\r
+                                       startTime     : startTime,\r
+                                       endTime       : endTime,\r
+                                       loop          : loop,\r
+                                       loopStartTime : loopStartTime\r
+                               } );\r
+                       };\r
+                       this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].play( this );\r
+                       return this;\r
                },\r
                \r
-               pause : function(){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].pause.call( this );\r
+               seek : function( seekTime ){\r
+                       var state = this.state();\r
+                       if( state.playing ){\r
+                               seekTime < state.endTime && this.state( { currentTime : seekTime } );\r
+                       } else {\r
+                               this.state( { startTime : seekTime } );\r
+                       };\r
+                       return this;\r
                },\r
                \r
-               stop : function(){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].stop.call( this );\r
+               pause : function(){\r
+                       this.state().playing && X_Audio_BACKENDS[ this._backend ].pause( this );\r
+                       return this;\r
                },\r
                \r
-               loop : function( v ){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].loop.call( this, v );\r
-               },\r
+               state : function( obj ){\r
+                       var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
 \r
-               state : function(){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].state.call( this );\r
+                       if( obj === undefined ){\r
+                               return backend ?\r
+                                       backend.state( this ) :\r
+                                       {\r
+                                       startTime     : -1,\r
+                                       endTime       : -1,\r
+                                       loopStartTime : -1,\r
+                                       currentTime   : -1,\r
+                                       loop          : false,\r
+                                       loaded        : false,\r
+                                       error         : false,\r
+                                       playing       : false,\r
+                                       \r
+                                       source        : this.source || '',\r
+                                       duration      : 0\r
+                                       };\r
+                       };\r
+                       backend && backend.state( this, obj );\r
+                       return this;\r
+               },              \r
+               \r
+               loop : function( v ){\r
+                       var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+                       if( v === undefined ){\r
+                               return backend && backend.state( this ).loop;\r
+                       };\r
+                       backend && backend.state( this, { loop : v } );\r
+                       return this;\r
                },\r
 \r
                volume : function( v ){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].volume.call( this, v );\r
-               },\r
-\r
-               startTime : function( time ){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].startTime.call( this, time );\r
+                       var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+                       if( v === undefined ){\r
+                               return backend && backend.state( this ).volume;\r
+                       };\r
+                       backend && backend.state( this, { volume : v } );\r
+                       return this;\r
                },\r
 \r
-               currentTime : function( time ){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].currentTime.call( this, time );\r
+               currentTime : function( v ){\r
+                       var backend = this._backend !== -1 && X_Audio_BACKENDS[ this._backend ];\r
+                       if( v === undefined ){\r
+                               return backend && backend.state( this ).currentTime;\r
+                       };\r
+                       backend && backend.state( this, { currentTime : v } );\r
+                       return this;\r
                },\r
 \r
                isPlaying : function(){\r
-                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].isPlaying.call( this );\r
+                       return this._backend !== -1 && X_Audio_BACKENDS[ this._backend ].state( this ).playing;\r
                }\r
                \r
        }\r
@@ -136,4 +186,103 @@ function X_AudioProxy_handleEvent( e ){
        };\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
+                                               };\r
+                                               break;\r
+                                       };                      \r
+                               } else {\r
+                                       continue;\r
+                               };\r
+                               k = 'startTime';\r
+                                       \r
+                       case 'startTime'     :\r
+                       case 'endTime'       :\r
+                       case 'loopStartTime' :\r
+                               v = X_AudioWrapper_timeStringToNumber( v );\r
+                               if( X.Type.isNumber( v ) && audioWrapper[ k ] !== v ){\r
+                                       audioWrapper[ k ] = v;\r
+                                       // 再生中の endTime, currentTime の変更\r
+                                       if( playing && k === 'endTime' ) end = 1;\r
+                               };\r
+                               break;\r
 \r
+                       case 'loop' :\r
+                               if( X.Type.isBoolean( v ) && audioWrapper[ k ] !== v ){\r
+                                       audioWrapper[ k ] = 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( 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.endTime <= audioWrapper.loopStartTime ||\r
+               audioWrapper.endTime  < audioWrapper.seekTime ||\r
+               audioWrapper.duration < audioWrapper.endTime\r
+       ){\r
+               //alert( '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
+       if( X.Type.isNumber( time ) ) return time;\r
+       if( !X.Type.isString( time ) || !time.length ) return;\r
+\r
+       ary = time.split( '.' );\r
+       ms  = parseInt( ( ary[ 1 ] + '000' ).substr( 0, 3 ) ) || 0;\r
+       \r
+       ary = ary[ 0 ].split( ':' );\r
+       if( 3 < ary.length ) return;\r
+       \r
+       switch( ary.length ){\r
+               case 0 :\r
+                       break;\r
+               case 1 :\r
+                       s = parseInt( ary[ 0 ] ) || 0;\r
+                       break;\r
+               case 2 :\r
+                       m = parseInt( ary[ 0 ] ) || 0;\r
+                       s = parseInt( ary[ 1 ] ) || 0;\r
+                       if( 60 <= s ) alert( 'invalid time string ' + time );\r
+                       break;\r
+               case 3 :\r
+                       h = parseInt( ary[ 0 ] ) || 0;\r
+                       m = parseInt( ary[ 1 ] ) || 0;\r
+                       s = parseInt( ary[ 2 ] ) || 0;\r
+                       if( 60 <= s ) alert( 'invalid time string ' + time );\r
+                       if( 60 <= m ) alert( 'invalid time string ' + time );\r
+                       break;\r
+               default :\r
+                       alert( 'invalid time string ' + time );\r
+       };\r
+       ms = ( h * 3600 + m * 60 + s ) * 1000 + ms;\r
+       return ms < 0 ? 0 : ms;\r
+};\r
index 341e8d4..ec0e809 100644 (file)
@@ -17,8 +17,7 @@ if( window.HTMLAudioElement ){
                };\r
        };\r
        \r
-       X_Audio_HTML5Audio = X_Class_override(\r
-               new X.EventDispatcher(),\r
+       X_Audio_HTML5Audio = \r
                {\r
                        backendName : 'HTML5 Audio',\r
                /*\r
@@ -49,11 +48,11 @@ if( window.HTMLAudioElement ){
                 *  - volume, muted iPhone(iOS4-6)、Android(2.3.6)では動作せず。\r
                 *  - FireFox3.6, Android 2.3.6については、src変更後、load()を呼び出さないと切り替わらなかった。iPhoneはload()が不要。\r
                 */     \r
-                       detect : function( source, ext ){\r
+                       detect : function( proxy, source, ext ){\r
                                var ok, mineType = 'audio/' + ext;\r
                                switch( ext ){\r
                                        case 'mp3' :\r
-                                               ok = X_UA.IE || X_UA.Chrome || X_UA.Safari; //( X_UA.OS === 'windows' && X_UA.Safari );\r
+                                               ok = X_UA.IE || X_UA.Chrome || ( X_UA.Windows && X_UA.Safari );\r
                                                mineType = 'audio/mpeg';\r
                                                break;\r
                                        case 'ogg' :\r
@@ -67,7 +66,7 @@ if( window.HTMLAudioElement ){
                                                ok = 2 <= X_UA.Gecko || 10.6 <= X_UA.Opera; // firefox4+(Gecko2+)\r
                                                break;\r
                                        case 'wav' :\r
-                                               ok = X_UA.Gecko || X_UA.Opera || X_UA.Safari; //( X_UA.OS === 'windows' && X_UA.Safari );\r
+                                               ok = X_UA.Gecko || X_UA.Opera || ( X_UA.Windows && X_UA.Safari );\r
                                                //mineType = 'audio/wav'; // audio/x-wav ?\r
                                                break;\r
                                        default :\r
@@ -79,56 +78,29 @@ if( window.HTMLAudioElement ){
                                        ok = X_Audio_rawAudio.canPlayType( mineType );\r
                                };\r
                                \r
-                               this.asyncDispatch( ok ? 'support' : 'nosupport' );\r
-                               \r
-                               return this;\r
+                               proxy.asyncDispatch( ok ? 'support' : 'nosupport' );\r
                        },\r
                        \r
                        register : function( proxy, source, option ){\r
                                X_Audio_HTML5Audio_LIVE_LIST.push( new X_Audio_HTML5AudioWrapper( proxy, source, option ) );\r
                        },\r
                        \r
-                       close : function(){\r
-                               return getHTML5AudioWrapper( this ).close();\r
+                       close : function( proxy ){\r
+                               getHTML5AudioWrapper( proxy ).close();\r
                        },\r
                        \r
-                       play : function( position ){\r
-                               return getHTML5AudioWrapper( this ).play( position );\r
-                       },\r
-                       \r
-                       pause : function(){\r
-                               return getHTML5AudioWrapper( this ).pause();\r
+                       play : function( proxy ){\r
+                               getHTML5AudioWrapper( proxy ).play();\r
                        },\r
                        \r
-                       stop : function(){\r
-                               return getHTML5AudioWrapper( this ).stop();\r
-                       },\r
-                       \r
-                       loop : function( v ){\r
-                               return getHTML5AudioWrapper( this ).loop( v );\r
-                       },\r
-       \r
-                       state : function(){\r
-                               return getHTML5AudioWrapper( this ).state();\r
+                       pause : function( proxy ){\r
+                               getHTML5AudioWrapper( proxy ).pause();\r
                        },\r
        \r
-                       volume : function( v ){\r
-                               return getHTML5AudioWrapper( this ).volume( v );\r
-                       },\r
-       \r
-                       startTime : function( time ){\r
-                               return getHTML5AudioWrapper( this ).startTime( time );\r
-                       },\r
-       \r
-                       currentTime : function( time ){\r
-                               return getHTML5AudioWrapper( this ).currentTime( time );\r
-                       },\r
-       \r
-                       isPlaying : function(){\r
-                               return getHTML5AudioWrapper( this ).isPlaying();\r
+                       state : function( proxy, obj ){\r
+                               return getHTML5AudioWrapper( proxy ).state( obj );\r
                        }\r
-               }\r
-       );\r
+               };\r
        \r
        X_Audio_BACKENDS.push( X_Audio_HTML5Audio );\r
        \r
@@ -139,21 +111,26 @@ if( window.HTMLAudioElement ){
                        \r
                        proxy           : null,\r
                        \r
-                       _closed         : true,\r
-                       _lastUserAction : '',\r
+                       startTime       : 0,\r
+                       endTime         : 1 / 0,\r
+                       loopStartTime   : 0,\r
+                       seekTime        : 0,\r
+                       duration        : 1 / 0,\r
                        \r
-                       _loop           : false,\r
-                       _startTime      : 0,\r
-                       _volume         : 0.5,\r
-                       _autoplay       : false,\r
+                       playing         : false,\r
+                       error           : 0,                    \r
+                       loop            : false,\r
+                       volume          : 0.5,\r
+\r
+            _timerID        : 0,\r
+                       \r
+                       _closed         : true,\r
                        \r
                        Constructor : function( proxy, source, option ){\r
                                this.proxy   = proxy;\r
                                this._closed = false;\r
                                \r
-                               if( option.loop )      this._loop      = true;\r
-                               if( option.startTime ) this._startTime = option.startTime;\r
-                               if( option.volume )    this._volume    = option.volume;\r
+                               X_AudioWrapper_updateStates( this, option );\r
                                \r
                                this._rawObject = X_Audio_rawAudio || new Audio( source );//X.Node.create( 'audio', { src : source } ).appendToRoot();//( X.X_Node_systemNode );\r
                                \r
@@ -170,7 +147,6 @@ if( window.HTMLAudioElement ){
 \r
                                //document.body.appendChild( this._rawObject );\r
 \r
-                               this._rawObject.volume   = this._volume;\r
                                this._rawObject.autoplay = false;\r
                                option.autoplay && X.Timer.once( 100, this, this.play );\r
                                \r
@@ -188,15 +164,24 @@ if( window.HTMLAudioElement ){
                         * http://uguisu.skr.jp/html/table3.html\r
                         */\r
                        handleEventProxy : function( e ){\r
-                               console.log(e.type);\r
+                               //console.log(e.type);\r
                                \r
                                switch( e.type ){\r
                                        case 'loadstart' :      //      ブラウザがコンテンツの検索を開始した場合に発生\r
                                                break;\r
                                        case 'progress' :       //      ブラウザがコンテンツの取得を実行した場合に発生\r
                                                //console.log( e.loaded + ' ' + e.total * 100 + '%' );\r
-                                               console.log( this._rawObject.buffered.end(0) / this._rawObject.duration * 100 + '%' );\r
+                                               this._rawObject.duration && console.log( this._rawObject.buffered.end(0) / this._rawObject.duration * 100 + '%' );\r
                                                break;\r
+                                       case 'loadedmetadata' : //      ブラウザがメディアリソースの長さと寸法を判定した場合に発生\r
+                                       case 'loadeddata' :     //      コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生\r
+                                       case 'canplay' :        //      今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生\r
+                                       case 'canplaythrough' : //      今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
+                                               this.duration = this._rawObject.duration * 1000;\r
+                                               this.endTime  = this.duration < this.endTime ? this.duration : this.endTime;\r
+                                               //console.log( this.duration );\r
+                                               break;\r
+       \r
                                        case 'suspend' :        //      ブラウザが意図的にコンテンツの取得を現在行っていない場合に発生(ダウンロードは未完了)\r
                                        case 'abort' :          //      ダウンロードの完了前にコンテンツの取得を停止した場合に発生(この停止はエラーによるものではない)\r
                                        case 'error' :          //      コンテンツの取得実行中にエラーが発生した場合に発生\r
@@ -204,19 +189,23 @@ if( window.HTMLAudioElement ){
                                        case 'stalled' :        //      ブラウザがコンテンツの取得を試みたが、データがまだ用意されていない場合に発生\r
                                        case 'play' :           //      再生が開始された。play()メソッドからの復帰後に発生する場合に発生\r
                                        case 'pause' :          //      再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生\r
-                                       case 'loadedmetadata' : //      ブラウザがメディアリソースの長さと寸法を判定した場合に発生\r
-                                       case 'loadeddata' :     //      コンテンツの表示を現在の再生位置で初めて行えるようになった場合に発生\r
+\r
                                        case 'waiting' :        //      次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生\r
                                        case 'playing' :        //      再生が開始された場合に発生\r
-                                       case 'canplay' :        //      今すぐに再生を再開できるが、バッファリングが不十分でコンテンツを最後まで表示できないと予測している場合に発生\r
-                                       case 'canplaythrough' : //      今すぐに再生を開始してもバッファリングで停止することなく最後まで表示できると予測している場合に発生\r
+\r
                                        case 'seeking' :        //      シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生\r
                                        case 'seeked' :         //      シークがfalseに変化した場合に発生\r
                                        case 'timeupdate' :     //      通常の再生が行われ現在の再生位置の変化が起こった場合に発生\r
                                                break;\r
                                        \r
                                        case 'ended' :\r
-                                               //!this._closed && this._lastUserAction !== 'stop' && this._loop && this.play();\r
+                                               if( !this._closed && this.loop ){\r
+                                                       this.play();\r
+                                               } else {\r
+                                                       this._timerID && X.Timer.remove( this._timerID );\r
+                                                       delete this._timerID;\r
+                                                       delete this.playing;\r
+                                               };\r
                                                break;\r
        \r
                                        case 'ratechange' :     // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生\r
@@ -229,90 +218,132 @@ if( window.HTMLAudioElement ){
                        },\r
                        \r
                        close : function(){\r
-                               // pool, proxy を外す\r
-                               //this.kill();\r
+                               // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】\r
+                               // http://ingaouhou.com/archives/3633\r
+                               // ・使い終わったインスタンスはload()しておくとやや安定\r
+                               this.playing && this.pause();\r
+                               delete this._closed;\r
+                               \r
+                               this._rawObject.src = '';\r
+                               this._rawObject.load();\r
                        },\r
                        \r
-                       play : function( position ){\r
+                       play : function( seekTime ){\r
+                               var begin, halfway;\r
+                               \r
                                // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
-                           if( this._closed ) return;\r
-                           this._lastUserAction = 'play';\r
-                       \r
-                           if( X_UA.Chrome ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
-                               // [!] delay\r
-                               X.Timer.once( 0, this, this._fixForChrome, [ this._rawObject.volume ] );\r
-                               this._rawObject.volume = 0;\r
-                           };\r
+                               if( this._closed ) return;\r
+\r
+                           begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;\r
+                           this._rawObject.currentTime = begin / 1000;\r
                            \r
-                           if( !this._rawObject.paused ){\r
-                               this.currentTime( this._startTime );\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
+                                   };\r
+                               this._rawObject.play();\r
+                               this.playing = true;\r
                            };\r
-                           this._rawObject.play();\r
-                       },\r
-                       \r
-                       // [CHROME][FIX] volume\r
-                       _fixForChrome : X_UA.Chrome && function( volume ){\r
-                               !this._closed && ( this._rawObject.volume = volume );\r
+\r
+                   halfway = this.endTime < this.duration;\r
+                   this._timerID && X.Timer.remove( this._timerID );\r
+                   \r
+                if( halfway ){\r
+                                       this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );\r
+                } else {\r
+                       delete this._timerID;\r
+                };\r
+                \r
+                               if( !this._interval ){\r
+                                       this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
+                               };\r
                        },\r
+                               \r
+                               // [CHROME][FIX] volume\r
+                               _fixForChrome : X_UA.Chrome && function(){\r
+                                       !this._closed && ( this._rawObject.volume = this.volume );\r
+                               },\r
+\r
+                               _onInterval : function(){\r
+                                       if( !this.playing ){\r
+                                               delete this._interval;\r
+                                               return X_Callback_UN_LISTEN;\r
+                                       };\r
+                                       this.proxy.dispatch( 'timeupdate' );\r
+                               },\r
+                               \r
+                               _onEnded : function(){\r
+                                       delete this._timerID;\r
+                           if( this.playing ){\r
+                               if( this.loop ){\r
+                                       this.play();\r
+                               } else {\r
+                                       console.log( '中断:' + this._rawObject.currentTime + ' ' + this.endTime );\r
+                                       this.pause();\r
+                                       this.dispatch( 'ended' );\r
+                               };\r
+                           };\r
+                               },\r
                        \r
                        pause : function(){\r
-                           if( !this._closed && !this._rawObject.error ){\r
-                               this._lastUserAction = 'pause';\r
-                               this._rawObject.pause();        \r
-                           };\r
-                       },\r
-                       \r
-                       stop : function(){\r
-                           if( !this._closed && !this._rawObject.error ){\r
-                               this._lastUserAction = 'stop';\r
+                               this._timerID && X.Timer.remove( this._timerID );\r
+                               delete this._timerID;\r
+                               \r
+                           if( this.palying && !this._rawObject.error ){\r
                                this._rawObject.pause();\r
-                               this.currentTime( this._startTime );\r
+                               delete this.playing;\r
                            };\r
                        },\r
-                       \r
-                       loop : function( v ){\r
-                           if( v === undefined ) return this._loop;\r
-                           this._rawObject.loop = this._loop = v;\r
-                       },\r
        \r
-                       state : function(){\r
-                           var paused = !!this._rawObject.paused,\r
-                               ended  = !!this._rawObject.ended;\r
-                       \r
-                           if( this._lastUserAction === 'stop' ){\r
-                               if( paused ){\r
-                                   paused = false;\r
-                                   ended  = true;\r
-                               };\r
-                           };\r
+                       state : function( obj ){\r
+                               var result;\r
+                               \r
+                               if( obj === undefined ){\r
+                                   return {\r
+                                       startTime     : this.startTime,\r
+                                       endTime       : this.endTime,\r
+                                       loopStartTime : this.loopStartTime,\r
+                                       currentTime   : this._rawObject.currentTime * 1000,\r
+                                       loop          : this.loop,\r
+                                       volume        : this.volume,\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
+                                       playing       : this.palying && !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended,                            \r
+                                       duration      : this.duration || 0\r
+                                   };                                  \r
+                               };\r
                        \r
-                           return {\r
-                               loop     : this._rawObject.loop,\r
-                               error    : this._rawObject.error    || 0,   // 0, 1 ~ 4\r
-                               paused   : paused,\r
-                               ended    : ended,\r
-                               source   : this._rawObject.src      || '',\r
-                               duration : this._rawObject.duration || 0\r
-                           };\r
-                       },\r
-       \r
-                       volume : function( v ){\r
-                           if( v === undefined ) return this.audio.volume;\r
-                           this._rawObject.volume = v;\r
-                       },\r
-       \r
-                       startTime : function( time ){\r
-                           if( time === undefined ) return this._startTime;\r
-                           this._startTime = time;\r
-                       },\r
-       \r
-                       currentTime : function( time ){\r
-                           if( time === undefined ) return this._rawObject.currentTime;\r
-                           this._rawObject.currentTime = time;\r
-                       },\r
-       \r
-                       isPlaying : function(){\r
-                           return !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended;\r
+                               result = X_AudioWrapper_updateStates( this, obj );\r
+                           \r
+                               if( result & 2 ){ // seek\r
+                       this.play( this.seekTime );\r
+                       delete this.seekTime;\r
+                               } else {\r
+                                       if( result & 1 ){\r
+                                               halfway = this.endTime < this.duration;\r
+                                               this._timerID && X.Timer.remove( this._timerID );\r
+                                               \r
+                                               if( halfway ){\r
+                                                       this._timerID = X.Timer.once( this.endTime - this._rawObject.currentTime * 1000, this, this._onEnded );                                                 \r
+                                               } else {\r
+                                                       delete this._timerID;\r
+                                               };\r
+                                       };\r
+                                       if( result & 4 ){\r
+                              this._rawObject.volume = this.volume;\r
+                                       };\r
+                               };\r
+                           \r
                        }\r
        \r
                }\r
index 484a900..14d6e84 100644 (file)
@@ -26,64 +26,35 @@ if( X.Pulgin.SilverlightEnabled ){
                };\r
        };\r
        \r
-       X_Audio_SLAudio = X_Class_override(\r
-               new X.EventDispatcher(),\r
+       X_Audio_SLAudio = \r
                {\r
                        backendName : 'Silverlight Audio',\r
 \r
-                       detect : function( source, ext ){\r
+                       detect : function( proxy, source, ext ){\r
                                var ok = ext === 'mp3' || ext === 'wma';\r
-                               \r
-                               this.asyncDispatch( ok ? 'support' : 'nosupport' );\r
-                               \r
-                               return this;\r
+                               proxy.asyncDispatch( ok ? 'support' : 'nosupport' );                            \r
                        },\r
                        \r
                        register : function( proxy, source, option ){\r
                                X_Audio_SLAudio_LIVE_LIST.push( new X_Audio_SLAudioWrapper( proxy, source, option ) );\r
                        },\r
                        \r
-                       close : function(){\r
-                               return getSLAudioWrapper( this ).close();\r
-                       },\r
-                       \r
-                       play : function( position ){\r
-                               return getSLAudioWrapper( this ).play( position );\r
+                       close : function( proxy ){\r
+                               return getSLAudioWrapper( proxy ).close();\r
                        },\r
                        \r
-                       pause : function(){\r
-                               return getSLAudioWrapper( this ).pause();\r
-                       },\r
-                       \r
-                       stop : function(){\r
-                               return getSLAudioWrapper( this ).stop();\r
+                       play : function( proxy ){\r
+                               return getSLAudioWrapper( proxy ).play();\r
                        },\r
                        \r
-                       loop : function( v ){\r
-                               return getSLAudioWrapper( this ).loop( v );\r
-                       },\r
-       \r
-                       state : function(){\r
-                               return getSLAudioWrapper( this ).state();\r
+                       pause : function( proxy ){\r
+                               return getSLAudioWrapper( proxy ).pause();\r
                        },\r
        \r
-                       volume : function( v ){\r
-                               return getSLAudioWrapper( this ).volume( v );\r
-                       },\r
-       \r
-                       startTime : function( time ){\r
-                               return getSLAudioWrapper( this ).startTime( time );\r
-                       },\r
-       \r
-                       currentTime : function( time ){\r
-                               return getSLAudioWrapper( this ).currentTime( time );\r
-                       },\r
-       \r
-                       isPlaying : function(){\r
-                               return getSLAudioWrapper( this ).isPlaying();\r
+                       state : function( proxy, obj ){\r
+                               return getSLAudioWrapper( proxy ).state( obj );\r
                        }\r
-               }\r
-       );\r
+               };\r
        \r
        X_Audio_BACKENDS.push( X_Audio_SLAudio );\r
        \r
@@ -97,20 +68,26 @@ if( X.Pulgin.SilverlightEnabled ){
                {\r
                        _isSilverlight  : true, // for X.EventDispatcher.listen\r
                proxy           : null,\r
+               \r
+                       startTime       : 0,\r
+                       endTime         : 0,\r
+                       loopStartTime   : 0,\r
+                       seekTime        : 0,\r
+                       duration        : 0,\r
+                       \r
+                       playing         : false,\r
+                       error           : 0,                    \r
+                       loop            : false,\r
+                       volume          : 0.5,\r
+               \r
                _onload         : '',\r
                _callback       : null,                 \r
                xnodeObject     : null,\r
-               \r
-                       _loop           : false,\r
                        _source         : '',\r
-                       _error          : 0,\r
                        _ended          : true,\r
                        _paused         : false,\r
-                       _volume         : 0.5,\r
-                       _startTime      : 0,\r
                        _lastUserAction : '',\r
                        _lastState      : '',\r
-                       _duration       : 0,\r
                        _interval       : 0, // setInterval timer id\r
                        \r
                        Constructor : function( proxy, source, option ){\r
@@ -132,6 +109,7 @@ if( X.Pulgin.SilverlightEnabled ){
                                \r
                                // TODO embed\r
                                this.proxy       = proxy;\r
+                               this._source     = source;\r
                        this._onload     = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );\r
                                this._callback   = window[ this._onload ] = X_Callback_create( this, this.onSLReady, [ option.autoplay ] );\r
                        this.xnodeObject = X_Node_body\r
@@ -145,16 +123,12 @@ if( X.Pulgin.SilverlightEnabled ){
                                            '<param name="background" value="#00000000">' +      // transparent\r
                                            '<param name="windowless" value="true">' +\r
                                            '<param name="source" value="#silverlightaudio">' +  // XAML ID\r
-                                           '<param name="onload" value="' + this._onload + '">'// + // bond to global\r
+                                           '<param name="onload" value="' + this._onload + '">' // + // bond to global\r
                                            //'<param value="2.0.31005.0" name="minRuntimeVersion">' +\r
                                            //'<param value="true" name="autoUpgrade">' +\r
                                            //'<param name="onerror" value="slerror">' // bond to global\r
                                        );\r
-                               \r
-                           this._loop       = option.loop;\r
-                           this._source     = source;\r
-                               if( option.volume       ) this._volume  = option.volume;\r
-                               if( option.startTime ) this._startTime = option.startTime;\r
+                               X_AudioWrapper_updateStates( this, option );\r
        \r
                                this.listenOnce( X.Event.KILL_INSTANCE );\r
                        },\r
@@ -167,14 +141,12 @@ if( X.Pulgin.SilverlightEnabled ){
                                X_Callback_correct( this._callback );\r
                                delete this._callback;\r
 \r
-                               //if( sender.findName('media') ) alert( 'exist' );\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
+                                               '<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
@@ -190,27 +162,31 @@ if( X.Pulgin.SilverlightEnabled ){
                                switch( e.type ){\r
                                        \r
                                        case 'MediaFailed' :\r
-                                               this._error = 4;\r
-                                               this._ended = true;\r
+                                               this.error   = 4;\r
+                                               this.playing = false;\r
+                                               this._ended  = true;\r
                                                this._paused = false;\r
-                               \r
                                                this.proxy.dispatch( 'error' ); // open failed\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;\r
-                               // TODO 'canplaythrough'\r
-                                               this.proxy.dispatch( 'canplay' );\r
+                                               this.duration = this._rawObject.NaturalDuration.Seconds * 1000;\r
+                                               this.endTime  = this.endTime || this.duration;\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
                                                break;\r
-                                       case 'MediaEnded' :\r
-                                               this.currentTime(this._startTime);\r
-                               \r
-                                               if (this._loop) {\r
-                                                       this._rawObject.play();\r
-                                               }\r
+\r
+                                       case 'MediaEnded' :                             \r
+                                               this.loop && this.playing && this.play();\r
                                                break;\r
+\r
                                        case 'CurrentStateChanged' :\r
-                                               lastState       = this._lastState,\r
+                                               lastState        = this._lastState,\r
                                                currentState = this._rawObject.CurrentState;\r
                                \r
                                                // ignore consecutive events or 'Closed' == 'Error'\r
@@ -220,63 +196,82 @@ if( X.Pulgin.SilverlightEnabled ){
                                                };\r
                                                this._lastState = currentState; // update last state\r
                                \r
-                                               switch (currentState) {\r
-                                                       case 'Buffering':\r
-                                                       case 'Opening':\r
-                                                                       break;\r
-                                       \r
+                                               switch( currentState ){\r
+                                                       case 'Buffering' :\r
+                                                       case 'Opening' :\r
+                                                               switch( this._lastUserAction ){\r
+                                                                       case 'play' :\r
+                                                                               this.proxy.dispatch( 'waiting' );\r
+                                                                               break;\r
+                                                                       case 'seek' :\r
+                                                                               this.proxy.dispatch( 'seeking' );\r
+                                                                               break;\r
+                                                                       case 'pause' :\r
+                                                                               break;\r
+                                                               };\r
+                                                               break;\r
+\r
                                                        // media.play(none supported file) -> 'Error'\r
                                                        // media.play(file not found)     -> 'Closed'\r
                                                        // media.load -> 'Error'\r
                                                        case 'Error':\r
                                                        case 'Closed':\r
-                                                                       this._error = 4;\r
-                                                                       this._ended = true;\r
-                                                                       this._paused = false;\r
-                                                                       this.proxy.dispatch( 'error' );\r
-                                                                       break;\r
-                                       \r
+                                                               this.error   = 4;\r
+                                                               this.playing = false;\r
+                                                               this._ended  = true;\r
+                                                               this._paused = false;\r
+                                                               this.proxy.dispatch( 'error' );\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
-                                                                       switch (this._lastUserAction) {\r
-                                                                               case 'play': // play() -> file end -> event('ended')\r
-                                                                                       this._ended = true;\r
-                                                                                       this._paused = false;\r
-                                                                                       this.proxy.dispatch( 'ended' );\r
-                                                                                       this.currentTime(this._startTime);\r
-                                                                                       break;\r
-                                                                               case 'pause':\r
-                                                                                       this._ended = false;\r
-                                                                                       this._paused = true;\r
-                                                                                       break;\r
-                                                                               case 'stop':\r
-                                                                                       this._ended = true;\r
-                                                                                       this._paused = false;\r
-                                                                       }\r
-                                                                       break;\r
-                                       \r
+                                                               this.playing = false;\r
+                                                               \r
+                                                               switch( this._lastUserAction ){\r
+                                                                       case 'play': // play() -> file end -> event('ended')\r
+                                                                       case 'seek':\r
+                                                                               this._ended  = true;\r
+                                                                               this._paused = false;\r
+                                                                               this.proxy.dispatch( 'ended' );\r
+                                                                               this._currentTime( this.startTime );\r
+                                                                               break;\r
+                                                                       case 'pause':\r
+                                                                               this._ended  = false;\r
+                                                                               this._paused = true;\r
+                                                                               break;\r
+                                                                       case 'stop':\r
+                                                                               this._ended  = true;\r
+                                                                               this._paused = false;\r
+                                                               };\r
+                                                               break;\r
+\r
                                                        // media.play -> 'Playing'\r
                                                        case 'Playing':\r
-                                                                       this._error = 0;\r
-                                                                       this._ended = false;\r
-                                                                       this._paused = false;\r
-                                                                       this.proxy.dispatch( 'playing' );\r
-                                                                       break;\r
-                                       \r
+                                                               this.error   = 0;\r
+                                                               this.playing = true;\r
+                                                               this._ended  = false;\r
+                                                               this._paused = false;\r
+                                                               this.proxy.dispatch( 'playing' );\r
+                                                               break;\r
+\r
                                                        // stop()\r
                                                        case 'Stopped':\r
-                                                                       this._ended = true;\r
-                                                                       this._paused = false;\r
-                                                                       this.currentTime(this._startTime);\r
-                                               }\r
+                                                               this.playing = false;\r
+                                                               this._ended  = true;\r
+                                                               this._paused = false;\r
+                                                               this._currentTime( this.startTime );\r
+                                                               break;\r
+                                               };\r
                                                break;\r
-                                       \r
+\r
                                        case X.Event.KILL_INSTANCE :\r
                                                if( this._onload ){\r
-                                                       window[ this._onload ] = null;\r
-                                                       this._callback.kill();\r
+                                                       // window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応\r
+                                                       delete window[ this._onload ];\r
+                                                       delete this._onload;\r
+                                                       X_Callback_correct( this._callback );\r
                                                };\r
                                                this.xnodeObject.destroy();\r
                                                break;\r
@@ -284,116 +279,126 @@ if( X.Pulgin.SilverlightEnabled ){
                        },\r
                        \r
                        close : function(){\r
-                               if (this._interval) {\r
-                                       X.Timer.remove( this._interval );\r
-                                       delete this._interval;\r
-                               };\r
+                               this.playing && this.pause();\r
                                this.proxy.dispatch( 'ended' );\r
                                this.kill();\r
                        },\r
                        \r
                        // SilverlightAudio.play\r
-                       play : function(){\r
-                               if (!this._error) {\r
-                                       this._lastUserAction = 'play';\r
-                                       if (this._ended) {\r
-                                               this.currentTime(this._startTime);\r
-                                       }\r
-                       \r
-                                       this._rawObject.play();\r
-                                       this.proxy.dispatch( 'play' );\r
-                       \r
-                                       if (!this._interval) {\r
-                                               this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
-                                       }\r
-                               }\r
-                       },\r
-                       \r
-                       _onInterval : function(){\r
-                               this.isPlaying() && this.proxy.dispatch( 'timeupdate' );\r
+                       play : function( seekTime ){\r
+                               var begin, halfway;\r
+                               \r
+                               // もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
+                               if( this.error ) return;\r
+\r
+                               begin = ( seekTime || seekTime === 0 ) ? seekTime : this.playing ? this.loopStartTime : this.startTime;\r
+                               this._lastUserAction = this.playing ? 'seek' : 'play';\r
+                               \r
+                           this._currentTime( begin );\r
+                           this._rawObject.Volume = this.volume;\r
+                           \r
+                           if( !this.playing ){\r
+                                   this._rawObject.play();\r
+                           this.proxy.dispatch( 'play' );\r
+                           \r
+                           this.playing = true;\r
+                           };\r
+                   \r
+                   halfway = this.endTime < this.duration;\r
+                   this._timerID && X.Timer.remove( this._timerID );\r
+                   \r
+                if( halfway ){\r
+                                       this._timerID = X.Timer.once( this.endTime - begin, this, this._onEnded );\r
+                } else {\r
+                       delete this._timerID;\r
+                };\r
+                \r
+                               if( !this._interval ){\r
+                                       this._interval = X.Timer.add( 1000, 0, this, this._onInterval );\r
+                               };\r
                        },\r
+                               \r
+                               _onInterval : function(){\r
+                                       if( !this.playing ){\r
+                                               delete this._interval;\r
+                                               return X_Callback_UN_LISTEN;\r
+                                       };\r
+                                       this.proxy.dispatch( 'timeupdate' );\r
+                               },\r
+                               \r
+                               _onEnded : function(){\r
+                                       delete this._timerID;\r
+                           if( this.playing ){\r
+                               if( this.loop ){\r
+                                       this.play();\r
+                               } else {\r
+                                       this.pause();\r
+                                       this.dispatch( 'ended' );                               \r
+                               };\r
+                           };\r
+                               },\r
                        \r
                        // SilverlightAudio.pause\r
                        pause : function(){\r
-                               if (!this._error) {\r
+                               if( !this.error ){\r
                                        this._lastUserAction = 'pause';\r
+                                       this.playing = false;\r
                                        this._paused = true;\r
-                                       this._ended = false;\r
+                                       this._ended  = false;\r
                                        this._rawObject.pause();\r
                                        this.proxy.dispatch( 'pause' );\r
-                               }\r
-                       },\r
-                       \r
-                       // SilverlightAudio.stop\r
-                       stop : function(){\r
-                               if (!this._error) {\r
-                                       this._lastUserAction = 'stop';\r
-                                       this._rawObject.stop();\r
-                                       this._ended = true;\r
-                               }\r
-                       },\r
-                       \r
-                       // SilverlightAudio.loop\r
-                       loop : function(value){ // @param Boolean: true is loop\r
-                                                                  // @return Boolean/void: true is loop\r
-                               if (value === undefined) {\r
-                                       return this._loop;\r
-                               }\r
-                               this._loop = value;\r
-                       },\r
-                       \r
-                       // SilverlightAudio.state\r
-                       state : function(){ // @return Hash: { loop, error, paused, ended, source, duration }\r
-                               return {\r
-                                       loop     : this._loop,\r
-                                       error    : this._error,\r
-                                       paused   : this._paused,\r
-                                       ended    : this._ended,\r
-                                       source   : this._source,\r
-                                       duration : this._duration\r
                                };\r
                        },\r
                        \r
-                       // SilverlightAudio.volume\r
-                       volume : function(value){ // @param Number: 0.0 ~ 1.0\r
-                                                                        // @return Number/void:\r
-                               if (value === undefined) {\r
-                                       return this._volume;\r
-                               }\r
-                               this._volume = value;\r
-                               this._rawObject.Volume = value;\r
-                       },\r
-                       \r
-                       // SilverlightAudio.startTime\r
-                       startTime : function(time){ // @param Number: time\r
-                                                                          // @return Number/void:\r
-                               if (time === undefined) {\r
-                                       return this._startTime;\r
-                               }\r
-                               this._startTime = time;\r
-                       },\r
-                       \r
-                       // SilverlightAudio.currentTime\r
-                       currentTime : function(time){ // @param Number: time\r
-                                                                                // @return Number/void:\r
-                               var position = this._rawObject.Position; // [!] create instance\r
+                       // SilverlightAudio.state\r
+                       state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
+                               var result;\r
                                \r
-                               if (time === undefined) {\r
-                                       return position.Seconds;\r
+                               if( obj === undefined ){\r
+                                   return {\r
+                                       startTime     : this.startTime,\r
+                                       endTime       : this.endTime,\r
+                                       loopStartTime : this.loopStartTime,\r
+                                       currentTime   : this._rawObject.Position.Seconds * 1000,\r
+                                       loop          : this.loop,\r
+                                       volume        : this.volume,\r
+                                       error         : this.error,\r
+                                       playing       : this.playing,\r
+                                       duration      : this.duration || 0 // this._rawObject.NaturalDuration.Seconds;\r
+                                   };\r
                                };\r
-\r
-                               position.Seconds = time; // set current time\r
                        \r
-                               this._rawObject.Position = position; // [!] reattach instance\r
+                               result = X_AudioWrapper_updateStates( this, obj );\r
+                           \r
+                               if( result & 2 ){ // seek\r
+                       this.play( this.seekTime );\r
+                       delete this.seekTime;\r
+                               } else {\r
+                                       if( result & 1 ){\r
+                                               halfway = this.endTime < this.duration;\r
+                                               this._timerID && X.Timer.remove( this._timerID );\r
+                                               \r
+                                               if( halfway ){\r
+                                                       this._timerID = X.Timer.once( this.endTime - this._rawObject.Position.Seconds * 1000, this, this._onEnded );\r
+                                               } else {\r
+                                                       delete this._timerID;\r
+                                               };\r
+\r
+                                       };\r
+                                       if( result & 4 ){\r
+                              this._rawObject.Volume = this.volume;\r
+                                       };\r
+                               };\r
                        },\r
                        \r
-                       // SilverlightAudio.isPlaying\r
-                       isPlaying : function(){ // @return Boolean:\r
-                               if (!this._error && !this._paused && !this._ended) {\r
-                                       return true;\r
+                               // SilverlightAudio.currentTime\r
+                               _currentTime : function( time ){ // @param Number: time\r
+                                       var position = this._rawObject.Position; // [!] create instance\r
+       \r
+                                       position.Seconds = time / 1000; // set current time\r
+                               \r
+                                       this._rawObject.Position = position; // [!] reattach instance\r
                                }\r
-                               return false;\r
-                       }\r
 \r
                }\r
        );\r