dua = n.userAgent,\r
dav = n.appVersion,\r
tv = parseFloat(dav),\r
+ sys = n.platform,\r
tridentToVer, i, j, v;\r
\r
console.log( ' userAgent : ' + dua );\r
console.log( '-' );\r
console.log( ' appVersion : ' + dav );\r
console.log( '-' );\r
- console.log( ' platform : ' + n.platform );\r
+ console.log( ' platform : ' + sys );\r
console.log( '-' );\r
\r
// TODO 3DS, DSi, WiiU\r
\r
- if( dua.indexOf( 'iPhone;' ) !== -1 || dua.indexOf( 'iPad;' ) !== -1 || dua.indexOf( 'iPod;' ) !== -1 ){\r
- \r
+ if( sys.indexOf( 'iP' ) === 0 ){\r
+\r
v = dua.split( 'OS ' )[ 1 ].split( '_' );\r
+ i = window.devicePixelRatio === 1;\r
\r
acme.iOSMajor = parseFloat( v[ 0 ] ) || 0;\r
acme.iOSMinor = parseFloat( v[ 1 ] ) || 0;\r
acme.iOSPatch = parseFloat( v[ 2 ] ) || 0;\r
- \r
+\r
acme.iOS = acme.iOSMajor + acme.iOSMinor / 10;\r
+\r
+ if( screen.width === screen.height * 1.5 || screen.width * 1.5 === screen.height ){\r
+ v = true; // 4:3 model\r
+ };\r
+ \r
+ if( sys === 'iPhone' ){\r
+ acme.iPhone = true;\r
+ if( v ) acme.iPhone_4s = true; // 4s以下\r
+ if( v && i ) acme.iPhone_3GS = true; // 3GS以下\r
+ alert( 'iPhone ' + ( acme.iPhone_3GS ? '3GS以下' : acme.iPhone_4s ? '4s以下' : '5以上' ) );\r
+ };\r
+ if( sys === 'iPad' ){\r
+ acme.iPad = true;\r
+ if( i ) acme.iPod_2Mini1 = true;\r
+ };\r
+ if( sys === 'iPod' ){\r
+ acme.iPod = true;\r
+ if( v ) acme.iPod_4 = true;\r
+ if( v && i ) acme.iPod_3 = true;\r
+ alert( 'iPod touch ' + ( acme.iPod_3 ? '3以下' : acme.iPod_4 ? '4以下' : '5以上' ) );\r
+ };\r
\r
console.log( '>> iOS : ' + acme.iOS );\r
} else\r
// http://user-agent-string.info/list-of-ua/os-detail?os=webOS\r
acme.webOS = true; // webOS\r
} else\r
- if( n.platform.indexOf( 'Win' ) + 1 ){\r
+ if( sys.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
+ if( sys === 'Win16' ) acme.Win16 = true;\r
+ if( sys === 'Win32' ) acme.Win32 = true;\r
+ if( sys === 'Win64' ) acme.Win64 = true;\r
+ if( sys === 'WinCE' ) acme.WinCE = true;\r
} else\r
- if( n.platform.indexOf( 'Mac' ) + 1 ){\r
+ if( sys.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
+ if( sys === 'MacPPC' || sys === 'MacPowerPC' ) acme.MacPPC = true;\r
+ if( sys === 'Mac68K' ) acme.Mac68K = true;\r
+ if( sys === 'MacIntel' ) acme.MacIntel = true;\r
} else\r
- if( n.platform.indexOf( 'Linux' ) + 1 ){\r
+ if( sys.indexOf( 'Linux' ) + 1 ){\r
console.log( 'Linux' );\r
acme.Linux = true;\r
\r
X.Logger.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );\r
return;\r
};\r
- X_Class_killPrivateFlag = false; // onKill 内で PrivateInstance.kill() を防ぐため\r
- \r
- // onKill() === false の場合、kill のキャンセル\r
- // private は false での キャンセル は無視される\r
+ X_Class_killPrivateFlag = false; // instance.kill() 内で PrivateInstance.kill() を防ぐため\r
\r
+ // X.EventDispatcher とそのサブクラスは kill() が呼ばれた通知を発行する。\r
+ // X.Event.BEFORE_KILL_INSTANCE はキャンセル可能(private な class は不可)\r
+ // X.Event.KILL_INSTANCE は、プロパティやイベントリスナの削除直前に発行\r
+ // X.Event.KILL_INSTANCE_CANCELED は kill() がキャンセルされた場合に発行。また dispatchループ中にkill()が呼ばれると一旦キャンセルされ発行。\r
+ // (flagを立ててdispatchの終わりにkillする)\r
if( this.instanceOf( X.EventDispatcher ) ){\r
//console.log( 'this.instanceOf( X.EventDispatcher )! ' + this._dispatching );\r
if( !def.isPrivate ){\r
};\r
this.dispatch( X.Event.KILL_INSTANCE );\r
this._listeners && X_EventDispatcher_systemUnlisten( this ); //.unlisten();\r
- } else\r
- if( X.Type.isFunction( instance.onKill ) && instance.onKill() === false && !def.isPrivate ){\r
- return;\r
};\r
\r
for( p in instance ){\r
list = X_Timer_TICKET_LIST,\r
i = 0,\r
l = list.length,\r
- limit = X_Timer_now() + X_Timer_INTERVAL_TIME / 2,\r
+ limit = now + X_Timer_INTERVAL_TIME / 2,\r
heavy,\r
q, f, c, r, uid;\r
\r
var last, now;\r
if( X_Timer_timerId ){\r
X_Timer_CLEAR_TIMEOUT( X_Timer_timerId );\r
- now = X_Timer_now();\r
- last = X_Timer_timeStamp + X_Timer_INTERVAL_TIME * X_Timer_waitTime - now;\r
- X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, 0 < last ? last : 0 );\r
+ now = X_Timer_now();\r
+ last = X_Timer_timeStamp + X_Timer_INTERVAL_TIME * X_Timer_waitTime - now;\r
+ X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, 0 < last ? last : 0 );\r
// 更新\r
X_Timer_timeStamp = now;\r
X_Timer_waitTime = last / X_Timer_INTERVAL_TIME | 0;\r
if( this._rawObject.timeout === undefined ){\r
this._timerID = X.Timer.once( timeout, this, this.onTimeout );\r
} else {\r
- delete this._timerID;\r
+ this._timerID = 0;\r
};\r
\r
// http://allabout.co.jp/gm/gc/24097/#1\r
case 'html' :\r
case 'htm' :\r
// svg, vml, xaml\r
- data = raw[ 'responseXML' ];\r
+ data = raw[ 'responseXML' ] || raw[ 'response' ] || raw[ 'responseText' ]; // とりあえず\r
break;\r
case 'blob' :\r
case 'arraybuffer' :\r
var X_Audio_WebAudio_context = window.webkitAudioContext || window.AudioContext,
X_Audio_WebAudioWrapper;
-if( X_Audio_WebAudio_context ){
+if( !X_UA.iPhone_4s && !X_UA.iPod_2Mini1 && !X_UA.iPod_4 && X_Audio_WebAudio_context ){
X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
// Firefox 用の対策,,,
if( time < 0 ) return;
} else {
- if( time < -16 ){
+ if( time < 0 ){
console.log( '> onEnd ' + time );
this._timerID = X.Timer.once( -time, this, this._onEnded );
return;
console.log( '[WebAudio] pause' );
+ this.seekTime = this.state().currentTime;
+
this._timerID && X.Timer.remove( this._timerID );
delete this._timerID;
delete this.playing;
},
state : function( obj ){
- var time = this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : 0,
- result;
+ var result;
if( obj === undefined ){
return {
playing : this.playing,
duration : this.duration,
- currentTime : time + this._startTime,
+ currentTime : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._playTime ) : this.seekTime,
error : this.error
};
};
\r
var X_Audio_HTMLAudio_playTrigger =\r
X_UA.iOS ? 'suspend' :\r
- X_UA.AndroidBrowser2 ? 'loadeddata' : //'stalled' : Android 2.3.5(SBM101SH) では stalled は発生しない,,,\r
+ X_UA.AndroidBrowser2 ? 'stalled' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,,\r
X_UA.AndroidBrowser4 ? 'loadeddata' : 'canplay',\r
- X_Audio_HTML5AudioWrapper,\r
- X_Audio_rawAudio;\r
- \r
-console.log( 'Event ' + X_Audio_HTMLAudio_playTrigger );\r
+ X_Audio_HTMLAudioWrapper,\r
+ X_Audio_rawAudio,\r
+ // Opera Mobile 12 android4.4.4 & 2.3.5 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する\r
+ X_Audio_HTMLAudioWrapper_currentTimeFix = !!X_UA.OperaMobile || !!X_UA.OperaTablet;\r
\r
if( window.HTMLAudioElement ){\r
\r
- X_Audio_HTML5AudioWrapper = X.EventDispatcher.inherits(\r
+ X_Audio_HTMLAudioWrapper = X.EventDispatcher.inherits(\r
'X.AV.HTML5AudioWrapper',\r
X.Class.POOL_OBJECT,\r
{\r
volume : 0.5,\r
\r
_timerID : 0,\r
- \r
+ _playTime : 0,\r
_closed : true,\r
_loaded : false,\r
\r
\r
case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生\r
case 'seeked' : // シークがfalseに変化した場合に発生\r
+ break;\r
+ \r
case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生\r
+ this._onEnded();\r
break;\r
\r
case 'ended' :\r
this.proxy.dispatch( 'looped' );\r
} else {\r
this._timerID && X.Timer.remove( this._timerID );\r
+ this.seekTime = 0;\r
delete this._timerID;\r
delete this.playing;\r
};\r
//e.type === 'canplaythrough' && console.dir( e );\r
this.proxy.dispatch( e );\r
\r
- if( X_Audio_HTMLAudio_playTrigger === e.type ){\r
+ if( X_Audio_HTMLAudio_playTrigger === e.type || ( X_UA.AndroidBrowser2 && e.type === 'loadeddata' ) ){\r
+ !this._loaded && this.autoplay && X.Timer.once( 16, this, this.play );\r
this._loaded = true;\r
- this.autoplay && X.Timer.once( 16, this, this.play );\r
console.log( 'Loaded! ' + e.type );\r
};\r
},\r
},\r
\r
play : function(){\r
- var begin, end, halfway;\r
+ var begin, end;\r
\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this._closed ) return;\r
this._rawObject.play();\r
this.playing = true;\r
};\r
-\r
- halfway = end < this.duration;\r
- this._timerID && X.Timer.remove( this._timerID );\r
\r
- if( halfway ){\r
- this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
- } else {\r
- delete this._timerID;\r
- };\r
+ if( X_Audio_HTMLAudioWrapper_currentTimeFix ){\r
+ //this._timerID && X.Timer.remove( this._timerID );\r
+ this._beginTime = begin;\r
+ this._playTime = X_Timer_now();\r
+ \r
+ if( end < this.duration ){\r
+ //this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
+ } else {\r
+ delete this._timerID;\r
+ }; \r
+ };\r
+\r
},\r
\r
// [CHROME][FIX] volume\r
},\r
\r
_onEnded : function(){\r
- var time;\r
+ var currentTime, time;\r
+ \r
delete this._timerID;\r
\r
if( this.playing ){\r
- \r
- time = this._rawObject.currentTime * 1000 - X_AudioWrapper_getEndTime( this ) | 0;\r
- if( time < -16 ){\r
- console.log( '> onEnd ' + time + ' ' + ( this._rawObject.currentTime * 1000 | 0 ) + '/' + X_AudioWrapper_getEndTime( this ) );\r
- this._timerID = X.Timer.once( -time, this, this._onEnded );\r
+ currentTime = X_Audio_HTMLAudioWrapper_currentTimeFix ? X_Timer_now() - this._playTime + this._beginTime : this._rawObject.currentTime * 1000 | 0;\r
+ time = currentTime - X_AudioWrapper_getEndTime( this );\r
+ if( time < 0 ){\r
+ console.log( '> onEnd ' + (-time) + ' ' + currentTime + '/' + X_AudioWrapper_getEndTime( this ) );\r
+ //if( X_Audio_HTMLAudioWrapper_currentTimeFix ) this._timerID = X.Timer.once( -time, this, this._onEnded );\r
return;\r
};\r
\r
},\r
\r
pause : function(){\r
- this._timerID && X.Timer.remove( this._timerID );\r
- delete this._timerID;\r
+ if( !this.playing ) return;\r
\r
- if( this.playing ){\r
- !this._rawObject.error && this._rawObject.pause();\r
- delete this.playing;\r
- };\r
+ this.seekTime = this.state().currentTime;\r
+ \r
+ //this._timerID && X.Timer.remove( this._timerID );\r
+ //delete this._timerID;\r
+ delete this._playTime;\r
+\r
+ !this._rawObject.error && this._rawObject.pause();\r
+ delete this.playing;\r
},\r
\r
state : function( obj ){\r
playing : this.playing, // && !this._rawObject.error && !this._rawObject.paused && !this._rawObject.ended, \r
duration : this.duration,\r
\r
- currentTime : this._rawObject.currentTime * 1000,\r
+ currentTime :\r
+ this.playing ?\r
+ ( X_Audio_HTMLAudioWrapper_currentTimeFix ?\r
+ X_Timer_now() - this._playTime + this._beginTime :\r
+ this._rawObject.currentTime * 1000 | 0 ) :\r
+ this.seekTime,\r
/*\r
http://www.w3schools.com/tags/av_prop_error.asp\r
1 = MEDIA_ERR_ABORTED - fetching process aborted by user\r
\r
if( result & 2 ){ // seek\r
this.play();\r
- } else {\r
- if( result & 1 ){\r
- end = X_AudioWrapper_getEndTime( this );\r
- halfway = end < this.duration;\r
- this._timerID && X.Timer.remove( this._timerID );\r
- \r
- if( halfway ){\r
- this._timerID = X.Timer.once( end - this._rawObject.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
+ //} else\r
+ //if( result & 1 ){\r
+ //if( X_Audio_HTMLAudioWrapper_currentTimeFix ){\r
+ // this.play(); \r
+ //};\r
+\r
+ } else\r
+ if( result & 4 ){\r
+ this._rawObject.volume = this.volume;\r
};\r
\r
}\r
\r
X_Audio_BACKENDS.push(\r
{\r
- backendName : 'HTML5 Audio',\r
+ backendName : 'HTML Audio',\r
/*\r
* HTML5 の audio 要素と video 要素でサポートされているメディアフォーマット\r
* https://developer.mozilla.org/ja/docs/Web/HTML/Supported_media_formats\r
proxy.asyncDispatch( ok ? 'support' : 'nosupport' );\r
},\r
\r
- klass : X_Audio_HTML5AudioWrapper\r
+ klass : X_Audio_HTMLAudioWrapper\r
\r
} );\r
\r
\r
if( X.Pulgin.SilverlightEnabled ){\r
\r
- // TODO X.Node.inherits\r
+ // X.Node.inherits はできない。_rawObject は <object> でなく silverlight\r
X_Audio_SLAudioWrapper = X.EventDispatcher.inherits(\r
'X.AV.SilverlightAudioWrapper',\r
X.Class.POOL_OBJECT,\r
// media.play(file not found) -> 'Closed'\r
// media.load -> 'Error'\r
case 'Error':\r
- case 'Closed':\r
this.error = 4;\r
+ case 'Closed':\r
+ this.error = this.error || 2;\r
this.playing = false;\r
this._ended = true;\r
this._paused = false;\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.seekTime = 0;\r
+ this._ended = true;\r
+ this._paused = false;\r
this.proxy.dispatch( 'ended' );\r
this._currentTime( this.startTime );\r
break;\r
\r
// SilverlightAudio.play\r
play : function(){\r
- var begin, end, halfway;\r
+ var begin, end;\r
\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this.error ) return;\r
this.playing = true;\r
};\r
\r
- halfway = end < this.duration;\r
this._timerID && X.Timer.remove( this._timerID );\r
\r
- if( halfway ){\r
+ if( end < this.duration ){\r
this._timerID = X.Timer.once( end - begin, this, this._onEnded );\r
} else {\r
delete this._timerID;\r
if( this.playing ){\r
\r
time = this._rawObject.Position.Seconds * 1000 - X_AudioWrapper_getEndTime( this ) | 0;\r
- if( time < -16 ){\r
+ if( time < 0 ){\r
console.log( '> onEnd ' + time );\r
this._timerID = X.Timer.once( -time, this, this._onEnded );\r
return;\r
\r
// SilverlightAudio.pause\r
pause : function(){\r
- if( !this.error ){\r
- this._lastUserAction = 'pause';\r
- this.playing = false;\r
- this._paused = true;\r
- this._ended = false;\r
- this._rawObject.pause();\r
- this.proxy.dispatch( 'pause' );\r
- };\r
+ if( this.error || !this.playing ) return;\r
+ \r
+ this._lastUserAction = 'pause';\r
+ this.seekTime = this.state().currentTime;\r
+ this.playing = false;\r
+ this._paused = true;\r
+ this._ended = false;\r
+ \r
+ this._rawObject.pause();\r
+ this.proxy.dispatch( 'pause' );\r
},\r
\r
// SilverlightAudio.state\r
loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
\r
- currentTime : this._rawObject.Position.Seconds * 1000,\r
+ currentTime : this.playing ? this._rawObject.Position.Seconds * 1000 : this.seekTime,\r
\r
\r
loop : this.loop,\r
var X_Audio_Sprite_shouldUse = window.HTMLAudioElement && ( X_UA.iOS || X_UA.AndroidBrowser || X_UA.OperaMobile || X_UA.OperaTablet ),\r
X_Audio_Sprite_needTouchFirst = !!X_UA.iOS,\r
X_Audio_Sprite_inTouchAction = false,\r
- X_Audio_Sprite_enableMultiTrack = !X_UA.iOS,\r
+ X_Audio_Sprite_enableMultiTrack = !( X_UA.iOS < 6 ),\r
X_Audio_Sprite_enableVolume = window.HTMLAudioElement && ( !X_UA.iOS && !X_UA.AndroidBrowser && !X_UA.OperaMobile && !X_UA.OperaTablet ),\r
X_Audio_Sprite_useVideoForMulti = 4 <= X_UA.AndroidBrowser,\r
X_Audio_Sprite_maxTracks = X_UA.iOS < 6 ? 1 : X_Audio_Sprite_useVideoForMulti ? 2 : 9,\r
\r
for( i = 0; i < n; ++i ){\r
if( i === 1 && X_Audio_Sprite_useVideoForMulti ){\r
- // use <Video>\r
+ // TODO use <Video>\r
+ tracks.push( X.Audio.create( urls, option ) );\r
} else {\r
tracks.push( X.Audio.create( urls, option ) );\r
};\r
};\r
\r
tracks[ n - 1 ].listenOnce( [ 'backendfound', 'nobackend' ], this, X_Audio_Sprite_handleEvent );\r
+ \r
+ X_Audio_Sprite_instance.numTracks = n;\r
},\r
\r
close : function(){\r
},\r
\r
load : function(){\r
- var wrapper = X_AudioProxy_getAudioWrapper( X_Audio_Sprite_TEMP.tracks[ 0 ] );\r
- //X_Audio_Sprite_inTouchAction = true;\r
- wrapper._rawObject.load();\r
- //X_Audio_Sprite_inTouchAction = false;\r
+ var tracks = X_Audio_Sprite_TEMP.tracks,\r
+ i = 0, l = tracks.length;\r
+ for( ; i < l; ++i ){\r
+ X_AudioProxy_getAudioWrapper( tracks[ i ] )._rawObject.load();\r
+ };\r
},\r
\r
/*\r
case 'backendfound' :\r
this.asyncDispatch( e );\r
e.target.unlisten( 'nobackend', this, X_Audio_Sprite_handleEvent );\r
- if( e.backendName === 'Web Audio' ){\r
+ \r
+ if( e.backendName === 'HTML Audio' ){\r
+ e.target.listen( [ X_Audio_HTMLAudio_playTrigger, 'loadeddata' ], this, X_Audio_Sprite_handleEvent );\r
+ } else {\r
e.target.listen( 'canplaythrough', this, X_Audio_Sprite_handleEvent );\r
};\r
break;\r
break;\r
\r
case 'canplaythrough' :\r
- this.asyncDispatch( e );\r
+ case X_Audio_HTMLAudio_playTrigger :\r
+ case 'loadeddata' :\r
+ this.asyncDispatch( 'audioSpriteCanPlay' );\r
break;\r
\r
case 'looped' :\r