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