リビジョン | 3c54df5e0d4f20eb32cfd73480cbc9ae8358ebbf (tree) |
---|---|
日時 | 2016-01-05 11:04:45 |
作者 | itozyun <itozyun@user...> |
コミッター | itozyun |
Version 0.6.205, bug fixes X.Net.Form, update X.HTMLAudio for iOS7+.
@@ -22,9 +22,10 @@ | ||
22 | 22 | * <h4>必須プロパティ</h4> |
23 | 23 | * <dl> |
24 | 24 | * <dt>url<dd>URL |
25 | - * <dt>type<dd>'xhr', 'jsonp', 'image', 'img' | |
25 | + * <dt>type<dd>'xhr', 'jsonp', 'form', 'image', 'img' | |
26 | 26 | * <dt>xhr<dd>URL { url : 'hoge', type : 'xhr' } の省略形 |
27 | 27 | * <dt>jsonp<dd>URL { url : 'hoge', type : 'jsonp' } の省略形 |
28 | + * <dt>form<dd>URL { url : 'hoge', type : 'form' } の省略形 | |
28 | 29 | * <dt>image, img<dd>URL { url : 'hoge', type : 'image' } の省略形 |
29 | 30 | * </dl> |
30 | 31 | * <h4>XHR 用プロパティ</h4> |
@@ -50,14 +51,16 @@ | ||
50 | 51 | * <dt>params<dd>url パラメータを object で渡すことが出来る。 |
51 | 52 | * <dt>callbackName<dd>callback(json) コールバック名が固定されている際に指定。または &callback=hoge 以外の名前でコールバックを指定する場合に params と callbackName に書いておく。url パラメータに callback が無く、callbackName もない場合、フレームワーク内で自動で設定される |
52 | 53 | * <dt>charset<dd>ページと異なるjsonpを読み込む場合に指定 'EUC-JP', 'Shift-JIS' 等 script タグの charset に入る。https://code.google.com/p/ajaxzip3/issues/detail?id=5 |
53 | - * <dt>useFireWall<dd>異なるドメインに jsonp を読み込んだ後、xdomain iframe 通信を使ってデータを受け取る。不正なコードの実行を防ぐことが出来る、未実装 | |
54 | + * <dt>useFireWall<dd>異なるドメインに jsonp を読み込んだ後、xdomain iframe 通信を使ってデータを受け取る。不正なコードの実行を防ぐことが出来る(はず)、未実装 | |
54 | 55 | * </dl> |
55 | 56 | * |
56 | 57 | * <h4>Form 用プロパティ</h4> |
57 | 58 | * <dl> |
58 | - * <dt>params<dd>url パラメータを object で渡すことが出来る。 | |
59 | 59 | * <dt>method<dd>'GET' or 'POST' |
60 | - * <dt>target<dd>'_self', '_parent', '_top' の場合、ページから離脱する。target を指定せず同一ドメインの場合 response に body.innerHTML が返る。 | |
60 | + * <dt>params<dd>パラメータ object は input タグの name & value に展開される。object を入れ子にすることはできない。 | |
61 | + * <dt>target<dd>'_self', '_parent', '_top' の場合、ページから離脱する。target を指定せず同一ドメインの場合 response に body.innerHTML が返る。TODO X.Window | |
62 | + * <dt>timeout<dd>ms タイムアウト時間、省略可能 | |
63 | + * <dt>charset<dd>未実装 | |
61 | 64 | * </dl> |
62 | 65 | * |
63 | 66 | * @alias X.Net |
@@ -155,8 +158,8 @@ X[ 'Net' ] = X_EventDispatcher[ 'inherits' ]( | ||
155 | 158 | opt[ 'auth' ] = auth; // auth は deep copy されるとまずい |
156 | 159 | }; |
157 | 160 | |
158 | - // params を url に追加 | |
159 | - if( opt[ 'params' ] ){ | |
161 | + // params を url に追加 但し form は除く | |
162 | + if( opt[ 'params' ] && type !== X_NET_TYPE_FORM ){ | |
160 | 163 | url = X_URL_create( url, opt[ 'params' ] ); |
161 | 164 | delete opt[ 'params' ]; |
162 | 165 | }; |
@@ -68,7 +68,6 @@ X_TEMP.X_JSONP_params = { | ||
68 | 68 | load : function( option ){ |
69 | 69 | //createURL |
70 | 70 | var url = option[ 'url' ], |
71 | - params = option[ 'params' ], | |
72 | 71 | callback = option[ 'callbackName' ], |
73 | 72 | charset = option[ 'charset' ], |
74 | 73 | json2Path = window.RegExp ? 'js/libs/json2.js' : 'js/libs/json2_regfree.js', |
@@ -79,9 +78,7 @@ X_TEMP.X_JSONP_params = { | ||
79 | 78 | if( !X_URL_isSameProtocol( url ) ){ |
80 | 79 | return X_JSONP[ 'asyncDispatch' ]( X_EVENT_ERROR ); |
81 | 80 | }; |
82 | - | |
83 | - url = X_URL_create( url, params ); | |
84 | - | |
81 | + | |
85 | 82 | if( !callback && !( callback = X_URL_paramToObj( url.split( '?' )[ 1 ] )[ 'callback' ] ) ){ |
86 | 83 | url += '&callback=cb'; |
87 | 84 | callback = 'cb'; |
@@ -1,6 +1,8 @@ | ||
1 | 1 | //{+netform"<form>によるGETとPOST"(動的に生成したフォームによるGETとPOST。)[+net,+ninjaiframe] |
2 | 2 | |
3 | -var X_FormSender_errorTimerID, X_FormSender_onloadCount = 0; | |
3 | +var X_FormSender_errorTimerID, | |
4 | + X_FormSender_isLeave, | |
5 | + X_FormSender_onloadCount = 0; | |
4 | 6 | |
5 | 7 | X_TEMP.X_FormSender_init = function(){ |
6 | 8 | X_FormSender = X_Class_override( X_NinjaIframe(), X_TEMP.X_FormSender_params ); |
@@ -13,7 +15,7 @@ X_TEMP.X_FormSender_init = function(){ | ||
13 | 15 | |
14 | 16 | /* |
15 | 17 | * form 構築時に "><script> といった文字列の挿入を禁止するために " を エスケープする |
16 | - * TODO 改行文字を消す | |
18 | + * TODO 改行文字を消す escape? | |
17 | 19 | */ |
18 | 20 | function X_FormSender_escapeQuote( str ){ |
19 | 21 |
@@ -21,28 +23,29 @@ function X_FormSender_escapeQuote( str ){ | ||
21 | 23 | }; |
22 | 24 | |
23 | 25 | X_TEMP.X_FormSender_params = { |
24 | - | |
26 | + | |
25 | 27 | _busy : false, |
26 | 28 | _canceled : false, |
27 | 29 | |
28 | - timeout : 1000, | |
29 | - isJump : false, // ページを離脱するか? | |
30 | - | |
31 | 30 | load : function( option ){ |
32 | 31 | //createURL |
33 | - var params = option[ 'params' ] || {}, | |
34 | - target = option[ 'target' ], | |
32 | + var params = option[ 'params' ] || {}, | |
33 | + target = option[ 'target' ], | |
34 | + timeout = option[ 'timeout' ], | |
35 | + // http://search.web-sun.com/zatu/charset.html | |
36 | + // charset = option[ 'charset' ], | |
35 | 37 | html, k; |
36 | 38 | |
37 | 39 | target = target === '_self' ? '_parent' : target === '_blank' ? '_self' : target || '_self', |
38 | 40 | html = [ |
41 | + // <meta http-equiv="Content-Type" content="text/html; charset=euc-jp"> | |
39 | 42 | '<form method="', X_FormSender_escapeQuote( option[ 'method' ] || 'GET' ), |
40 | 43 | '" action="', X_FormSender_escapeQuote( option[ 'url' ] || '' ), |
41 | 44 | '" target="', X_FormSender_escapeQuote( target ), |
42 | 45 | '">' ]; |
43 | 46 | |
44 | - if( target === '_top' || target === '_parent' ) X_FormSender.isJump = true; | |
45 | - if( 0 <= option[ 'timeout' ] ) X_FormSender.timeout = option[ 'timeout' ]; | |
47 | + X_FormSender_isLeave = target === '_top' || target === '_parent'; | |
48 | + | |
46 | 49 | |
47 | 50 | for( k in params ){ |
48 | 51 | // TODO 使用すべきでない name |
@@ -51,11 +54,15 @@ X_TEMP.X_FormSender_params = { | ||
51 | 54 | }; |
52 | 55 | |
53 | 56 | html.push( '</form><script>document.forms[0].submit();</script>' ); |
54 | - | |
57 | + | |
55 | 58 | X_FormSender |
56 | 59 | [ 'refresh' ]( html.join( '' ) ) |
57 | 60 | [ 'listen' ]( [ 'ninjaload', 'ninjaerror' ], X_FormSender_iframeListener ); |
58 | - | |
61 | + | |
62 | + if( 0 < timeout ){ | |
63 | + X_FormSender_errorTimerID = X_FormSender[ 'asyncDispatch' ]( timeout, { type : X_EVENT_ERROR, 'timeout' : true } ); | |
64 | + }; | |
65 | + | |
59 | 66 | X_FormSender._busy = true; |
60 | 67 | }, |
61 | 68 |
@@ -66,8 +73,9 @@ X_TEMP.X_FormSender_params = { | ||
66 | 73 | |
67 | 74 | reset : function(){ |
68 | 75 | X_FormSender._busy = X_FormSender._canceled = false; |
69 | - X_FormSender[ 'unlisten' ]( [ 'ninjaload', 'ninjaerror' ], X_FormSender_iframeListener ); | |
70 | - X_FormSender[ 'refresh' ]( '' ); | |
76 | + X_FormSender | |
77 | + [ 'unlisten' ]( [ 'ninjaload', 'ninjaerror' ], X_FormSender_iframeListener ) | |
78 | + [ 'refresh' ]( '' ); | |
71 | 79 | X_FormSender_errorTimerID && X_Timer_remove( X_FormSender_errorTimerID ); |
72 | 80 | X_FormSender_errorTimerID = X_FormSender_onloadCount = 0; |
73 | 81 | } |
@@ -78,18 +86,13 @@ function X_FormSender_iframeListener( e ){ | ||
78 | 86 | |
79 | 87 | switch( e.type ){ |
80 | 88 | case 'ninjaload' : |
81 | - if( X_FormSender.isJump ){ | |
89 | + if( X_FormSender_isLeave ){ | |
82 | 90 | return; |
83 | 91 | }; |
84 | 92 | |
85 | 93 | if( ++X_FormSender_onloadCount === 1 ){ |
86 | - X_FormSender_errorTimerID = X_FormSender[ 'asyncDispatch' ]( X_FormSender.timeout, { type : X_EVENT_ERROR, 'timeout' : true } ); | |
87 | - | |
88 | - // TODO レスポンスの html にアクセスしたい場合 | |
89 | - // TODO samedomain or xiframe-sender | |
90 | - | |
91 | 94 | idoc = this[ '_rawObject' ].contentDocument || this._iwin.document, |
92 | - | |
95 | + | |
93 | 96 | X_FormSender[ 'asyncDispatch' ]( { type : X_EVENT_SUCCESS, response : idoc && idoc.body ? idoc.body.innerHTML : '' } ); |
94 | 97 | }; |
95 | 98 | break; |
@@ -79,7 +79,9 @@ if( X_Audio_constructor ){ | ||
79 | 79 | |
80 | 80 | |
81 | 81 | var X_WebAudio_context = // 4s 以下ではない iPad 2G または iPad mini 1G 以下ではない, iPod touch 4G 以下ではない |
82 | - !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && | |
82 | + !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && | |
83 | + // iOS7 以上で HTML Audio が鳴らない問題を見ていくよ | |
84 | + // !X_UA[ 'iOS' ] && | |
83 | 85 | // Android2 + Gecko で WebAudio が極めて不安定 |
84 | 86 | !( X_UA[ 'Fennec' ] && X_UA[ 'Android' ] < 3 ) && |
85 | 87 | // AOSP でも WebAudio を不完全に実装するものがある, touch の有無も不明のため一律に切ってしまう |
@@ -166,7 +168,7 @@ if( X_WebAudio_context ){ | ||
166 | 168 | _onDecodeSuccess : function( buffer ){ |
167 | 169 | this.onDecodeSuccess && this._onDecodeComplete(); |
168 | 170 | |
169 | - if ( !buffer ) { | |
171 | + if( !buffer ){ | |
170 | 172 | this.errorState = 2; |
171 | 173 | this[ 'asyncDispatch' ]( X_EVENT_COMPLETE ); |
172 | 174 | return; |
@@ -198,6 +200,7 @@ if( X_WebAudio_context ){ | ||
198 | 200 | unregister : function( webAudio ){ |
199 | 201 | var list = this.webAudioList, |
200 | 202 | i = list.indexOf( webAudio ); |
203 | + | |
201 | 204 | if( 0 < i ){ |
202 | 205 | list.splice( i, 1 ); |
203 | 206 | if( !list.length ){ |
@@ -289,7 +292,6 @@ if( X_WebAudio_context ){ | ||
289 | 292 | |
290 | 293 | if ( !buffer ) { |
291 | 294 | this.error = loader.errorState; |
292 | - | |
293 | 295 | this.disatcher[ 'dispatch' ]({ |
294 | 296 | type : X_EVENT_ERROR, |
295 | 297 | error : loader.errorState, |
@@ -39,8 +39,9 @@ | ||
39 | 39 | |
40 | 40 | var |
41 | 41 | X_HTMLAudio, |
42 | - | |
43 | - // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6 | |
42 | + // iOS7.1, 8.3 で確認.seeking -> seeked の間の currentTime の値が全くあてにならないので無視する。 | |
43 | + X_HTMLAudio_seekingFixIOS = 7 <= X_UA[ 'iOS' ], | |
44 | + // ended が発生しない timeupdate 内で play() を呼ぶ (未検証) 不具合確認は iOS4,6 iOS7.1,8.3ではpause->ended起きてる 但し iOS7.1 でも 6 と同じ症状になることがある | |
44 | 45 | X_HTMLAudio_endedFixIOS = X_UA[ 'iOS' ] < 7, |
45 | 46 | // Android 2.3.5 で ended 時に audio.src='';audio.src=src;audio.load() を実施。 2.3.4 でも問題なし。 |
46 | 47 | X_HTMLAudio_endedFixAOSP2 = X_UA[ 'AOSP' ] < 3, |
@@ -59,17 +60,21 @@ var | ||
59 | 60 | * win opera12 volume, mute の変更が2度目以降できない |
60 | 61 | */ |
61 | 62 | X_HTMLAudio_volumeEnabled = !( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) && !X_UA[ 'Opera' ], |
62 | - // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正 | |
63 | + // Gecko PC + Android でseek時に再生がしばしば止まる問題の修正、iOS8でも確認 | |
63 | 64 | X_HTMLAudio_needPlayForSeek = X_UA[ 'iOS' ] || X_UA[ 'Gecko' ], |
64 | 65 | // |
65 | 66 | X_HTMLAudio_pauseFix = 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ), // XP + Opera12 のみ? |
66 | 67 | |
67 | 68 | X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || 4.2 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || X_UA[ 'WinPhone' ] || ( X_UA[ 'Blink' ] && X_UA[ 'Android' ] ), |
68 | 69 | |
69 | - X_HTMLAudio_playTrigger = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ? 'canplay' : X_UA[ 'iOS' ] ? 'suspend' : X_UA[ 'Blink' ] < 32 ? 'stalled' : 'canplaythrough', | |
70 | + X_HTMLAudio_playTrigger = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ? 'canplay' : | |
71 | + X_UA[ 'iOS' ] < 8 ? 'suspend' : // iOS7.x以下 | |
72 | + X_UA[ 'iOS' ] ? 'loadedmetadata' : // iOS8以上は | |
73 | + X_UA[ 'Blink' ] < 32 ? 'stalled' : 'canplaythrough', | |
70 | 74 | |
71 | - X_HTMLAudio_durationFix = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) || | |
72 | - ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || ( X_UA[ 'Blink' ] < 36 && X_UA[ 'Android' ] ), | |
75 | + X_HTMLAudio_durationFix = // iOS8.1(シュミレータでは不要) | |
76 | + X_UA[ 'iOS' ] < 8 || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) || | |
77 | + ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || ( X_UA[ 'Blink' ] < 36 && X_UA[ 'Android' ] ), | |
73 | 78 | |
74 | 79 | X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ], |
75 | 80 |
@@ -109,6 +114,8 @@ if( X_Audio_constructor ){ | ||
109 | 114 | |
110 | 115 | _endedFixON : false, |
111 | 116 | |
117 | + _seekingFixON : false, | |
118 | + | |
112 | 119 | 'Constructor' : function( disatcher, source, option ){ |
113 | 120 | var raw; |
114 | 121 |
@@ -146,7 +153,8 @@ if( X_Audio_constructor ){ | ||
146 | 153 | //'loadstart', 'load', |
147 | 154 | 'progress', //'error', |
148 | 155 | // 'suspend', 'abort', 'emptied', 'stalled', |
149 | - // 'play', 'pause', 'seeked', 'ratechange', 'volumechange', | |
156 | + // 'play', 'pause', 'ratechange', 'volumechange', | |
157 | + 'seeked', | |
150 | 158 | 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', |
151 | 159 | 'playing', 'waiting', 'seeking', |
152 | 160 | 'durationchange', 'timeupdate', 'ended' ] ); |
@@ -161,7 +169,7 @@ if( X_Audio_constructor ){ | ||
161 | 169 | 'durationchange', 'timeupdate', 'ended' ], this.onDebug ); |
162 | 170 | |
163 | 171 | if( X_HTMLAudio_endedFixAOSP2 || X_HTMLAudio_endedFixAOSP4 ){ |
164 | - raw.loop = true; // loop を使えば ended で止まること回避できるかも 但し ended イベントが起きなくなる | |
172 | + raw.loop = true; // loop を使えば ended で止まること回避できる 但し ended イベントが起きなくなる | |
165 | 173 | }; |
166 | 174 | |
167 | 175 | if( X_HTMLAudio_need1stTouch ){ |
@@ -252,6 +260,9 @@ if( X_Audio_constructor ){ | ||
252 | 260 | |
253 | 261 | // TODO firefox で 短い音声でtimeupdate, ended が発火しない <- 最後の音に無音部分を追加する |
254 | 262 | case 'timeupdate' : // 通常の再生が行われ現在の再生位置の変化が起こった場合に発生 |
263 | + if( this._seekingFixON ){ | |
264 | + eventType = X_EVENT_MEDIA_SEEKING; | |
265 | + } else | |
255 | 266 | if( this._durationFixPhase === 8 ){ |
256 | 267 | this._durationFixPhase = 0; |
257 | 268 | this._readyState |= 1; |
@@ -271,9 +282,13 @@ if( X_Audio_constructor ){ | ||
271 | 282 | end = X_Audio_getEndTime( this ) + this._shortPlayFixTime; |
272 | 283 | //console.log( now + ' / ' + end ); |
273 | 284 | if( ( 0 + end <= 0 + now ) || // 0+ なぜか iem9 で必要,,, |
274 | - ( now < this._lastCurrentTime ) ){ // loop した場合 | |
285 | + ( now < this._lastCurrentTime && now < 2000 ) ){ | |
286 | + //( ( X_HTMLAudio_endedFixAOSP2 || X_HTMLAudio_endedFixAOSP4 ) && ( now < this._lastCurrentTime && now < 1000 ) ) ){ | |
287 | + // loop して0付近に戻った場合 | |
288 | + // iOS8.4 ではこのタイミングで now が last より 0.1秒後退している場合がある | |
289 | + // iOS7.1 ではもっと小さい場合がある,,, | |
275 | 290 | if( this.autoLoop ){ |
276 | - console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) ); | |
291 | + console.log( '☆★☆ 曲の最後に到達 @timeupdate now-end:' + ( now - end ) + ' now:' + now + ' last:' + this._lastCurrentTime ); | |
277 | 292 | ended = true; |
278 | 293 | //if( X_HTMLAudio_endedFixIOS ) actualEnded = true; |
279 | 294 | } else { |
@@ -310,16 +325,20 @@ if( X_Audio_constructor ){ | ||
310 | 325 | eventType = !this._durationFixPhase && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING; |
311 | 326 | //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生 |
312 | 327 | //case 'pause' : // 再生が一時停止された。pauseメソッドからの復帰後に発生する場合に発生 |
313 | - //case 'seeked' : | |
314 | 328 | //case 'ratechange' : // defaultPlaybackRate属性とplaybackRate属性のどちらかが更新された場合に発生 |
315 | 329 | //case 'volumechange' : // volume属性とmuted属性のどちらかが変化した場合に発生 |
316 | 330 | break; |
317 | 331 | |
332 | + case 'seeked' : | |
333 | + if( this._seekingFixON ) this._seekingFixON = false; | |
334 | + break; | |
335 | + | |
318 | 336 | case 'waiting' : // 次のフレームが利用不可のため再生を停止したが、そのフレームがやがて利用可能になると想定している場合に発生 |
319 | 337 | eventType = X_EVENT_MEDIA_WAITING; |
320 | 338 | break; |
321 | 339 | case 'seeking' : // シークがtrueに変化し、イベントを発生させるのに十分な時間がシーク操作にかかっている場合に発生 |
322 | 340 | eventType = X_EVENT_MEDIA_SEEKING; |
341 | + if( X_HTMLAudio_seekingFixIOS ) this._seekingFixON = true; | |
323 | 342 | break; |
324 | 343 | }; |
325 | 344 |