From 527f50d3e1930a335a6525b5c973ab27fe385868 Mon Sep 17 00:00:00 2001 From: itozyun Date: Mon, 19 Oct 2015 00:13:30 +0900 Subject: [PATCH] Version 0.6.179, fix X.Audio & X.AudioSprite. --- 0.6.x/js/01_core/00_builtin.js | 79 --------------------- 0.6.x/js/01_core/02_XUA.js | 133 ++++++++++++++++++++--------------- 0.6.x/js/01_core/04_XObject.js | 93 ++++++++++++++++++++++-- 0.6.x/js/01_core/13_XClass.js | 6 ++ 0.6.x/js/01_core/14_XEvent.js | 8 ++- 0.6.x/js/01_core/16_XTimer.js | 21 +++--- 0.6.x/js/01_core/21_XViewPort.js | 9 +-- 0.6.x/js/02_dom/02_XNodeFlags.js | 2 + 0.6.x/js/02_dom/07_XNodeList.js | 8 ++- 0.6.x/js/02_dom/09_XHTMLParser.js | 2 +- 0.6.x/js/02_dom/20_XNode.js | 17 ++--- 0.6.x/js/02_dom/22_XTreeBuilder.js | 24 +++---- 0.6.x/js/03_plugin/00_XPlugin.js | 30 ++------ 0.6.x/js/05_util/02_XJSON.js | 1 + 0.6.x/js/05_util/04_XXML.js | 41 ++++++----- 0.6.x/js/06_net/01_XNetXHR.js | 57 +++++++++++++-- 0.6.x/js/07_audio/01_XWebAudio.js | 7 +- 0.6.x/js/07_audio/02_XHTMLAudio.js | 119 ++++++++++++++++--------------- 0.6.x/js/07_audio/10_XAudioSprite.js | 116 ++++++++++++++---------------- 19 files changed, 418 insertions(+), 355 deletions(-) diff --git a/0.6.x/js/01_core/00_builtin.js b/0.6.x/js/01_core/00_builtin.js index 17b1dc3..e483e4e 100644 --- a/0.6.x/js/01_core/00_builtin.js +++ b/0.6.x/js/01_core/00_builtin.js @@ -274,82 +274,3 @@ if (window.ActiveXObject ? !Number.prototype.toFixed : (!navigator.taintEnabled }; })(); */ -/* - * Safari の JavaScript の不備 - * http://nanto.asablo.jp/blog/2006/01/13/209495 - * - * web.paulownia.jp - JavaScriptとクロージャ - * https://web.archive.org/web/20070526063400/http://web.paulownia.jp/script/oop/closure.html - * MacOSX 10.3のsafariにはhasOwnPropertyが実装されていないので、独自に追加する必要があります。 - * - * prototype汚染問題でhasOwnPropertyを使わないクロスブラウザな方法 - * http://os0x.hatenablog.com/entry/20080901/1220272509 - */ -/* -Object.prototype.hasOwnProperty || (Object.prototype.hasOwnProperty = function( p ){ - var proto = this.constructor && this.constructor.prototype, - __p__ = proto && proto.__proto__, - v = this[ p ], - r = false; - - if( __p__ ) proto.__proto__ = null; - - if( p in this ){ - if( v !== v ){ - if( proto && ( p in proto ) && proto[ p ] !== proto[ p ] ){ // proto[ p ] is NaN - proto[ p ] = 0; // different value - r = this[ p ] !== this[ p ]; // isNaN? - proto[ p ] = v; // set NaN - } else { - r = true; - }; - } else - if( proto && p in proto && proto[ p ] === v ){ - // this と proto に同名で同値が書かれている可能性あり - proto[ p ] = v + ' '; // different value - r = v === this[ p ]; - proto[ p ] = v; - } else { - r = true; - }; - }; - - if( __p__ ) proto.__proto__ = __p__; - - return r; - }); */ -/* -Object.prototype.hasOwnProperty || (Object.prototype.hasOwnProperty = function( p ){ - var proto = this.constructor && this.constructor.prototype, - __p__ = proto && proto.__proto__, - r = false,//!!( __p__ && ( proto.__proto__ = null ) ) - _pro_, v, isNaN; - - if( __p__ ) proto.__proto__ = null; - if( this.__proto__ ){ - _pro_ = this.__proto__; - this.__proto__ = null; - }; - - if( p === '__proto__' ){ - r = !!_pro_; - } else { - v = this[ p ]; - isNaN = v !== v; - - if( p in this ){ - if( proto && p in proto && ( proto[ p ] === v ) ^ isNaN ){ //true + false, false + true - // this と proto に同名で同値が書かれている可能性あり - proto[ p ] = v + ' '; // different value - r = ( v === this[ p ] ) ^ isNaN; // true + false, false + true - proto[ p ] = v; - } else { - r = true; - }; - }; - }; - - if( __p__ ) proto.__proto__ = __p__; - if( _p_ ) this.__proto__ = _pro_; - return r; - }); */ \ No newline at end of file diff --git a/0.6.x/js/01_core/02_XUA.js b/0.6.x/js/01_core/02_XUA.js index 0d03ee6..c639e70 100644 --- a/0.6.x/js/01_core/02_XUA.js +++ b/0.6.x/js/01_core/02_XUA.js @@ -262,6 +262,10 @@ var X_UA = X[ 'UA' ] = {}, */ X_UA[ 'Android' ] = X_UA[ 'AndroidMajor' ] + X_UA[ 'AndroidMinor' ] / 10; console.log( '>> Android : ' + X_UA[ 'Android' ] ); + } else + if( dua.indexOf( 'Android;' ) !== -1 ){ + // Fennec 用 + X_UA[ 'Android' ] = 2; }; }; @@ -338,12 +342,15 @@ var X_UA = X[ 'UA' ] = {}, X_UA[ 'Trident' ] = parseFloat(dav.split('Trident/')[1]) || 0; tridentToVer = X_UA[ 'Trident' ] ? ( X_UA[ 'Trident' ] + 4 | 0 ) : X_UA[ '_IE' ]; - /** - * IE10 以上の互換モードを使用している場合、そのバージョン - * @alias X.UA.IEHost - * @type {number} - */ - if( tridentToVer !== X_UA[ '_IE' ] ) X_UA[ 'IEHost' ] = tridentToVer; + + if( tridentToVer !== X_UA[ '_IE' ] ){ + /** + * IE10 以上の互換モードを使用している場合、そのバージョン + * @alias X.UA.IEHost + * @type {number} + */ + X_UA[ 'IEHost' ] = tridentToVer; + }; /** * documentモードを考慮したIEのバージョン * @alias X.UA.IE @@ -570,7 +577,7 @@ var X_UA = X[ 'UA' ] = {}, }; console.log( '>> Gecko : ' + X_UA[ 'Gecko' ] ); - }; + } else // Android 標準ブラウザ AOSP と ChromeWeb View, Sブラウザがある if( ( v = X_UA[ 'Android' ] ) && @@ -812,7 +819,9 @@ X_UA_ATagWrapDiv = (function( e, h ){ console.log( 'HTML5? ' + X_UA_ATagWrapDiv ); */ -var X_Script_VBS_ENABLED = X_UA[ 'Windows' ] && !X_UA[ 'WinCE' ] && !X_UA[ 'WinPhone' ] && X_UA[ 'IE' ] < 11; +var X_Script_VBS_ENABLED = X_UA[ 'Windows' ] && !X_UA[ 'WinCE' ] && !X_UA[ 'WinPhone' ] && X_UA[ 'IE' ] < 11, + // 構文のサポート instanceof, in(for-in ではない), try-catch. JS version 1.5以上 + X_Script_gte15 = !( X_UA[ 'IE' ] < 5.5 ) && ( new Function( 'f,a', 'try{return f.apply({},a)}catch(e){}' ) ); /** * js バージョン間の際を吸収 @@ -824,51 +833,63 @@ X[ 'Script' ] = { 'tryCatch' : X_Script_try }; -if( X_UA[ 'IE' ] < 9 && X_Script_VBS_ENABLED ){ - (function( lines ){ - var s = document.createElement( 'script' ); - X_elmHead.appendChild( s ); - s.type = 'text/vbscript'; - s.text = lines.join( '\n' ); - - })( [ - 'Function vba_testAXO(v)', - 'On Error Resume Next', - 'Set ax = CreateObject(v)', - 'If Err.Number Then', - 'ax = 1', - 'End If', - 'Err.Clear', - 'vba_testAXO = ax', - 'End Function' /*, - - 'Function vba_try(jsFunc,a1,a2,a3,a4)', - 'On Error Resume Next', - 'Set rs = jsFunc(a1,a2,a3,a4)', - 'If Err.Number != 0 Then', - 'rs = 0', - 'End If', - 'Err.Clear', - 'vba_try = rs', - 'End Function' */ - ] ); - +if( X_Script_VBS_ENABLED ){ + X_Script_gte15 || document.write( '' ); + // TODO Object のメンバを辿る vba + // byte Array を扱う vba }; function X_Script_try( func, args ){ - if( X_UA[ 'IE' ] < 5.5 ){ + if( !X_Script_gte15 ){ //return func.apply( {}, args ); return; }; - return ( new Function( 'f,a', 'try{return f.apply({},a)}catch(e){}' ) )( func, args || [] ); + return X_Script_gte15( func, args || [] ); }; function X_Script_createActiveXObjectSafty( name ){ - if( X_UA[ 'IE' ] < 9 ){ + if( !X_Script_gte15 ){ if( X_Script_VBS_ENABLED ){ - console.log( window[ 'vba_testAXO' ]( name ) + ' ' + name ); - return !window[ 'vba_testAXO' ]( name ) && X_Script_createActiveXObject( name ); + // console.log( window[ 'vbs_testAXO' ]( name ) + ' ' + name ); + return !window[ 'vbs_testAXO' ]( name ) && X_Script_createActiveXObject( name ); }; return X_Script_createActiveXObject( name ); }; @@ -880,18 +901,18 @@ function X_Script_createActiveXObject( name ){ return new ActiveXObject( name ); }; -// TODO 構文のサポート instanceof, in, try catch - -if( X_UA[ 'IE' ] < 7 ){ // error @ NN7.2 - X_UA[ 'IE4' ] || X_UA[ 'IE5' ] || X_UA[ 'MacIE' ] ? - document.execCommand && document.execCommand( 'BackgroundImageCache', false, true ) : - ( - X_Script_try( function(){ document.execCommand( 'BackgroundImageCache', false, true ); return 1; } ) && - /** - * ie7 以下で実行する document.execCommand( 'BackgroundImageCache', false, true ) の失敗。 - * bonus: hotfix for IE6 SP1 (bug KB823727) - * multipleIEs IE6 standalone 版では不可, IE5.5 は可,,, - * @alias X.UA.ieExeComError */ - ( X_UA[ 'ieExeComError' ] = true ) - ); +/* + * http://archiva.jp/web/html-css/ie6_background_flickr.html + * hover時の背景画像ちらつきに対処する + * この問題はIE6固有の問題であり、他のモダンブラウザやIE5等では発現しない。 + */ +if( X_UA[ 'IE6' ] && // error @ NN7.2 + !X_Script_try( function(){ document.execCommand( 'BackgroundImageCache', false, true ); return 1; } ) ){ + /** + * ie6 のみで実行する document.execCommand( 'BackgroundImageCache', false, true ) の失敗。 + * bonus: hotfix for IE6 SP1 (bug KB823727) + * multipleIEs IE6 standalone 版では不可, IE5.5 は可,,, + * @alias X.UA.ieExeComError */ + X_UA[ 'ieExeComError' ] = true; }; + diff --git a/0.6.x/js/01_core/04_XObject.js b/0.6.x/js/01_core/04_XObject.js index 9ee8197..329973d 100644 --- a/0.6.x/js/01_core/04_XObject.js +++ b/0.6.x/js/01_core/04_XObject.js @@ -7,12 +7,12 @@ * @param {object} obj * @return {boolean} name が定義されている(値が undefined や null でも) -> true */ -var X_Object_inObject = X_UA[ 'IE' ] < 5.5 ? // TODO JScript で判定 - (function( name, obj, p ){ - if( obj[ name ] ) return true; // quick +var X_Object_inObject = !X_Script_gte15 ? // TODO JScript で判定 + (function( name, obj, _ ){ name += ''; // 数値も許可 - for( p in obj ){ - if( p === name ) return true; + if( obj[ name ] ) return true; // quick + for( _ in obj ){ + if( _ === name ) return true; }; return false; }) : @@ -170,3 +170,86 @@ function X_Object_find( obj, selector ){ return obj; }; +// TODO X.Object.own( obj, name ) +/* + * Safari の JavaScript の不備 + * http://nanto.asablo.jp/blog/2006/01/13/209495 + * + * web.paulownia.jp - JavaScriptとクロージャ + * https://web.archive.org/web/20070526063400/http://web.paulownia.jp/script/oop/closure.html + * MacOSX 10.3のsafariにはhasOwnPropertyが実装されていないので、独自に追加する必要があります。 + * + * prototype汚染問題でhasOwnPropertyを使わないクロスブラウザな方法 + * http://os0x.hatenablog.com/entry/20080901/1220272509 + */ +/* +Object.prototype.hasOwnProperty || (Object.prototype.hasOwnProperty = function( p ){ + var proto = this.constructor && this.constructor.prototype, + __p__ = proto && proto.__proto__, + v = this[ p ], + r = false; + + if( __p__ ) proto.__proto__ = null; + + if( p in this ){ + if( v !== v ){ + if( proto && ( p in proto ) && proto[ p ] !== proto[ p ] ){ // proto[ p ] is NaN + proto[ p ] = 0; // different value + r = this[ p ] !== this[ p ]; // isNaN? + proto[ p ] = v; // set NaN + } else { + r = true; + }; + } else + if( proto && p in proto && proto[ p ] === v ){ + // this と proto に同名で同値が書かれている可能性あり + proto[ p ] = v + ' '; // different value + r = v === this[ p ]; + proto[ p ] = v; + } else { + r = true; + }; + }; + + if( __p__ ) proto.__proto__ = __p__; + + return r; + }); */ +/* +Object.prototype.hasOwnProperty || (Object.prototype.hasOwnProperty = function( p ){ + var proto = this.constructor && this.constructor.prototype, + __p__ = proto && proto.__proto__, + r = false,//!!( __p__ && ( proto.__proto__ = null ) ) + _pro_, v, isNaN; + + if( __p__ ) proto.__proto__ = null; + if( this.__proto__ ){ + _pro_ = this.__proto__; + this.__proto__ = null; + }; + + if( p === '__proto__' ){ + r = !!_pro_; + } else { + v = this[ p ]; + isNaN = v !== v; + + if( p in this ){ + if( proto && p in proto && ( proto[ p ] === v ) ^ isNaN ){ //true + false, false + true + // this と proto に同名で同値が書かれている可能性あり + proto[ p ] = v + ' '; // different value + r = ( v === this[ p ] ) ^ isNaN; // true + false, false + true + proto[ p ] = v; + } else { + r = true; + }; + }; + }; + + if( __p__ ) proto.__proto__ = __p__; + if( _p_ ) this.__proto__ = _pro_; + return r; + }); */ + + + diff --git a/0.6.x/js/01_core/13_XClass.js b/0.6.x/js/01_core/13_XClass.js index bb0a857..f5cdc03 100644 --- a/0.6.x/js/01_core/13_XClass.js +++ b/0.6.x/js/01_core/13_XClass.js @@ -584,6 +584,11 @@ function X_Class_actualConstructor( f, args ){ def.live && def.live.push( instance ); + if( ( X_UA[ 'AOSP' ] < 3 || X_UA[ 'iOS' ] < 5 ) && instance.constructor !== klass ){ + console.log( '------- constructor の不一致!' ); // Android2.3.7 + instance.constructor = klass; + }; + obj = def.Constructor ? def.Constructor.apply( instance, args ) : def.SuperConstructor && @@ -593,6 +598,7 @@ function X_Class_actualConstructor( f, args ){ instance[ 'kill' ](); return obj; }; + return instance; }; diff --git a/0.6.x/js/01_core/14_XEvent.js b/0.6.x/js/01_core/14_XEvent.js index e2a5cda..0741a18 100644 --- a/0.6.x/js/01_core/14_XEvent.js +++ b/0.6.x/js/01_core/14_XEvent.js @@ -110,7 +110,9 @@ var // 内部イベント X_EVENT_NEED_AUTH = 49, - X_Event_last = 49; + X_EVENT_DEBUG = 50, + + X_Event_last = 50; // in_page_jump // on_screen_keyboard_show @@ -221,7 +223,9 @@ X[ 'Event' ] = /** @lends X.Event */ 'MEDIA_WAITING' : X_EVENT_MEDIA_WAITING, 'MEDIA_SEEKING' : X_EVENT_MEDIA_SEEKING, - 'NEED_AUTH' : X_EVENT_NEED_AUTH + 'NEED_AUTH' : X_EVENT_NEED_AUTH, + + 'DEBUG' : X_EVENT_DEBUG }; X_TEMP.onSystemReady.push( diff --git a/0.6.x/js/01_core/16_XTimer.js b/0.6.x/js/01_core/16_XTimer.js index 681d5f8..2c00513 100644 --- a/0.6.x/js/01_core/16_XTimer.js +++ b/0.6.x/js/01_core/16_XTimer.js @@ -44,9 +44,7 @@ var */ X_Timer_now = Date.now || ( function(){ return +new Date; } ), - X_Timer_SET_TIMEOUT = window.setTimeout, - X_Timer_CLEAR_TIMEOUT = window.clearTimeout, - + // TODO X.AF.request, X.AF.cancel // http://uupaa.hatenablog.com/entry/2012/02/01/083607 // Firefox 4 partial (request only), Mobile Firefox5 ready (request only), Firefox 11 ready (cancel impl) X_Timer_REQ_ANIME_FRAME = @@ -183,6 +181,8 @@ var * @alias X.Timer */ X[ 'Timer' ] = { + // TODO IE4 の resolution は 64ms + 'RESOLUTION' : X_Timer_INTERVAL_TIME, 'now' : X_Timer_now, @@ -216,6 +216,7 @@ X[ 'Timer' ] = { function X_Timer_add( time, opt_count, args1, args2, args3 ){ var list = X_Timer_TICKET_LIST, hash, obj; + time = time < X_Timer_INTERVAL_TIME ? 1 : time / X_Timer_INTERVAL_TIME | 0; // 正の数で使える「Math.floor(x)」を「(x | 0)」に; if( !X_Type_isNumber( opt_count ) ){ @@ -290,7 +291,7 @@ X[ 'Timer' ] = { }; if( X_UA[ 'IE4' ] || X_UA[ 'MacIE' ] ){ - X.Timer[ '_' ] = X_Timer_onTimeout; + X[ 'Timer' ][ '_' ] = X_Timer_onTimeout; X_Timer_onTimeout = 'X.Timer._()'; }; @@ -368,7 +369,7 @@ function X_Timer_update(){ i = list.length, n; if( i === 0 ){ - X_Timer_timerId && X_Timer_CLEAR_TIMEOUT( X_Timer_timerId ); + X_Timer_timerId && clearTimeout( X_Timer_timerId ); X_Timer_timerId = 0; return; }; @@ -379,12 +380,12 @@ function X_Timer_update(){ if( n < X_Timer_waitTime || X_Timer_timerId === 0 ){ if( X_Timer_timerId ){ - X_Timer_CLEAR_TIMEOUT( X_Timer_timerId ); - n -= ( X_Timer_now() - X_Timer_timeStamp ) / X_Timer_INTERVAL_TIME | 0; + clearTimeout( X_Timer_timerId ); + n -= ( X_Timer_now() - X_Timer_timeStamp ) / X_Timer_INTERVAL_TIME; 0 <= n || ( n = 0 ); // 負の数は 0 に }; X_Timer_timeStamp = X_Timer_now(); - X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, X_Timer_INTERVAL_TIME * n ); + X_Timer_timerId = setTimeout( X_Timer_onTimeout, X_Timer_INTERVAL_TIME * n | 0 ); X_Timer_waitTime = n; }; }; @@ -405,10 +406,10 @@ if( X_UA[ 'iOS' ] ){ window.addEventListener( 'scroll', function(){ var last, now; if( X_Timer_timerId ){ - X_Timer_CLEAR_TIMEOUT( X_Timer_timerId ); + clearTimeout( X_Timer_timerId ); now = X_Timer_now(); last = X_Timer_timeStamp + X_Timer_INTERVAL_TIME * X_Timer_waitTime - now; - X_Timer_timerId = X_Timer_SET_TIMEOUT( X_Timer_onTimeout, 0 < last ? last : 0 ); + X_Timer_timerId = setTimeout( X_Timer_onTimeout, 0 < last ? last : 0 ); // 更新 X_Timer_timeStamp = now; X_Timer_waitTime = last / X_Timer_INTERVAL_TIME | 0; diff --git a/0.6.x/js/01_core/21_XViewPort.js b/0.6.x/js/01_core/21_XViewPort.js index c83d9de..a89df05 100644 --- a/0.6.x/js/01_core/21_XViewPort.js +++ b/0.6.x/js/01_core/21_XViewPort.js @@ -51,7 +51,7 @@ X_ViewPort = X_Class_override( X_ViewPort[ 'dispatch' ]( X_EVENT_UNLOAD ); //alert('unload'); X_ViewPort_document[ 'kill' ](); - this[ 'kill' ](); + X_ViewPort[ 'kill' ](); //X_System[ 'dispatch' ]( X_EVENT_SHUT_DOWN ); break; @@ -415,7 +415,7 @@ X[ 'ViewPort' ] = { }; X_TEMP.onDomContentLoaded = function(){ - var s, size, html, head, body; + var html, head, body; console.log( '> X_TEMP.onDomContentLoaded rs:' + X_ViewPort_readyState ); @@ -537,13 +537,10 @@ X[ 'ViewPort' ] = { console.log( '------------------->' ); if( document[ 'webkitHidden' ] !== undefined ){ - console.log( '--> has webkitvisibilitychange' ); X_EventDispatcher_systemListen( X_ViewPort_document, 'webkitvisibilitychange', X_ViewPort ); } else if( document[ 'hidden' ] !== undefined ){// iOS 7+ - console.log( '--> has visibilitychange' ); X_EventDispatcher_systemListen( X_ViewPort_document, 'visibilitychange', X_ViewPort ); - document.onvisibilitychange = function(){ console.log( '!!!!!!!!!!!!!!!!' ) }; } else if( document[ 'msHidden' ] !== undefined ){ X_EventDispatcher_systemListen( X_ViewPort_document, 'msvisibilitychange', X_ViewPort ); @@ -553,12 +550,10 @@ console.log( '------------------->' ); }; if( window[ 'onpageshow' ] !== undefined ){ - console.log( '-------------------> pageshow, pagehide' ); X_EventDispatcher_systemListen( X_ViewPort, [ 'pageshow', 'pagehide' ] ); }; if( document[ 'onfocusin' ] !== undefined ){ - console.log( '-------------------> focusin, focusout' ); // https://github.com/ai/visibilityjs/blob/master/lib/visibility.fallback.js X_EventDispatcher_systemListen( X_ViewPort_document, [ 'focusin', 'focusout' ], X_ViewPort ); }; diff --git a/0.6.x/js/02_dom/02_XNodeFlags.js b/0.6.x/js/02_dom/02_XNodeFlags.js index 0a9fad8..c6faba9 100644 --- a/0.6.x/js/02_dom/02_XNodeFlags.js +++ b/0.6.x/js/02_dom/02_XNodeFlags.js @@ -61,6 +61,8 @@ var X_NodeFlags_DESTROYED = 0x0, }; return 0; })(), + + X_NodeFlags_SYSTEM_NODE = 2 << 27, X_Node_BITMASK_RESET_STYLE = ( ( 2 << 29 ) - 1 + ( 2 << 29 ) ) ^ ( X_NodeFlags_STYLE_IS_DISPLAY_NONE | diff --git a/0.6.x/js/02_dom/07_XNodeList.js b/0.6.x/js/02_dom/07_XNodeList.js index be45a3b..8e8702e 100644 --- a/0.6.x/js/02_dom/07_XNodeList.js +++ b/0.6.x/js/02_dom/07_XNodeList.js @@ -53,15 +53,17 @@ X_NodeList.prototype[ 'each' ] = function( func /* opt_args... */ ){ X_TEMP.onSystemReady.push( function( sys ){ var target = X_NodeList.prototype, src = Node.prototype, - p, v, multi; + p, v; for( p in src ){ //if( X_EMPTY_OBJECT[ p ] ) continue; v = src[ p ]; if( X_Type_isFunction( v ) && !target[ p ] ){ - target[ p ] = multi = new Function( [ + target[ p ] = new Function( [ 'var a=arguments,f=X.Node.prototype.', p, ',t=this,i,l=t.length;', 'if(l)', - 'for(i=0;i' ].join( '' ) ); + } else + if( that[ '_flags' ] & X_NodeFlags_IS_SVG ){ + elm = document.createElementNS( 'http://www.w3.org/2000/svg', that[ '_tag' ].toLowerCase() ); } else { - if( that[ '_flags' ] & X_NodeFlags_IS_SVG ){ - that[ '_rawObject' ] = elm = document.createElementNS( 'http://www.w3.org/2000/svg', that[ '_tag' ].toLowerCase() ); - } else { - that[ '_rawObject' ] = elm = document.createElement( that[ '_tag' ] ); - }; + elm = document.createElement( that[ '_tag' ] ); }; + that[ '_rawObject' ] = elm; + // IE には要素追加のタイミングで起こるメモリリークがありここで追加 if( !X_Node_addTreeAfterChildren ){ nextElement ? diff --git a/0.6.x/js/02_dom/22_XTreeBuilder.js b/0.6.x/js/02_dom/22_XTreeBuilder.js index 868261e..530b094 100644 --- a/0.6.x/js/02_dom/22_XTreeBuilder.js +++ b/0.6.x/js/02_dom/22_XTreeBuilder.js @@ -41,12 +41,12 @@ if( X_UA[ 'MacIE' ] ){ e.appendChild( node ); parent.removeChild( e ); node.parentNode = null; - document.body.appendChild( node ); + X_elmBody.appendChild( node ); parent.replaceChild( document.createComment( '' ), node ); */ - document.body.appendChild( node ); + X_elmBody.appendChild( node ); return; /* - if( parent.parentNode !== document.body ){ + if( parent.parentNode !== X_elmBody ){ var clone = parent.cloneNode( true ); for( var i = 0, l = parent.childNodes.length; i < l; ++i ){ if( parent.childNodes[ i ] !== node ){ @@ -112,7 +112,7 @@ X_TEMP._onPreInit = X_UA_DOM.W3C ? (function(){ var r = X_Node_body, - body = r[ '_rawObject' ], + body = X_elmBody, copy, i, l, node, html, elmProgress; @@ -124,7 +124,7 @@ X_TEMP._onPreInit = // textarea の内容を控えて、消す。xnode tree 構築後に復帰。でないと、html パースでこける //X_UA[ 'MacIE' ] && alert( body.innerHTML ); // cleanup tree - function cleanUpTree( elm, skip, head ){ + function cleanUpTree( elm, skip ){ var nodes = X_Array_copy( elm.childNodes ), i = 0, l = nodes.length, @@ -135,8 +135,7 @@ X_TEMP._onPreInit = case 1 : tag = node.tagName.toUpperCase(); if( X_Dom_DTD_MOVE_TO_HEAD[ tag ] ){ - head = head || document.getElementsByTagName( 'head' )[ 0 ]; - head.appendChild( node ); + X_elmHead.appendChild( node ); continue; } else if( X_TEMP._isCleanupTarget( node ) ){ @@ -144,7 +143,7 @@ X_TEMP._onPreInit = continue; } else { // pre タグ以下はスペースの置換は行わない - node.childNodes && node.childNodes.length && cleanUpTree( node, skip || X_Dom_DTD_SKIP_CLEANUP_TAGS[ tag ], head ); + node.childNodes && node.childNodes.length && cleanUpTree( node, skip || X_Dom_DTD_SKIP_CLEANUP_TAGS[ tag ] ); }; textNode = null; break; @@ -198,8 +197,6 @@ X_TEMP._onPreInit = elmProgress.style.cssText = 'position:absolute;top:0;left:0;z-index:9999;width:0;height:0.5em;background:#00f;overflow:hidden;'; elmProgress.setAttribute( 'style', 'position:absolute;top:0;left:0;z-index:9999;width:0;height:0.5em;background:#00f;overflow:hidden;' ); - X_TEMP._body = body; - X_HTMLParser_asyncParse( html, true ) [ 'listen' ]( X_EVENT_PROGRESS, X_TEMP._handleEvent ) [ 'listenOnce' ]( X_EVENT_SUCCESS, X_TEMP._handleEvent ); @@ -224,8 +221,6 @@ X_TEMP._onPreInit = body.insertAdjacentHTML( 'BeforeEnd', '
' ); X_TEMP.elmProgress = document.all[ elmProgress ]; - X_TEMP._body = body; - X_HTMLParser_asyncParse( html, true ) [ 'listen' ]( X_EVENT_PROGRESS, X_TEMP._handleEvent ) [ 'listenOnce' ]( X_EVENT_SUCCESS, X_TEMP._handleEvent ); @@ -247,10 +242,9 @@ X_TEMP._handleEvent = function( e ){ xnodes.push.apply( xnodes, e.xnodes ); elmProgress.style.width = '100%'; - X_TEMP.asyncCreateTree( X_Node_body, X_TEMP._body.childNodes || X_TEMP._body.children, elmProgress ); + X_TEMP.asyncCreateTree( X_Node_body, X_elmBody.childNodes || X_elmBody.children, elmProgress ); delete X_TEMP._onPreInit; - delete X_TEMP._body; delete X_TEMP.elmProgress; break; }; @@ -298,7 +292,7 @@ X_TEMP.asyncCreateTree = function( parent, elems, elmProgress, async ){ continue; }; - if( startTime + 16 <= X_Timer_now() ){ + if( startTime + X_Timer_INTERVAL_TIME <= X_Timer_now() ){ current.i = i; if( async ){ async.current = i < l && current; diff --git a/0.6.x/js/03_plugin/00_XPlugin.js b/0.6.x/js/03_plugin/00_XPlugin.js index cac6fa8..3114a87 100644 --- a/0.6.x/js/03_plugin/00_XPlugin.js +++ b/0.6.x/js/03_plugin/00_XPlugin.js @@ -25,10 +25,6 @@ var X_Pulgin_FLASH_VERSION = })() : 0, - X_Pulgin_FLASH_ENABLED = - X_UA[ 'ActiveX' ] ? !!X_Pulgin_FLASH_VERSION : - X_Object_find( navigator, 'mimeTypes>application/x-shockwave-flash>enabledPlugin' ), - /* * ie11 の 互換モード(8,7)では、Silverlight が動作しない? */ @@ -36,7 +32,7 @@ var X_Pulgin_FLASH_VERSION = !X_UA[ 'IE' ] || !X_UA[ 'ActiveX' ] ? parseFloat( X_Object_find( navigator, 'plugins>Silverlight Plug-In>version' ) || 0 ) : X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] ? (function(){ var obj = X_Script_createActiveXObjectSafty( 'AgControl.AgControl' ), - i = obj ? 5 : 0, f; + i = obj ? 5 : 0; for( ; i; --i ){ if( obj[ 'IsVersionSupported' ]( i + '.0' ) ) return i; @@ -45,10 +41,6 @@ var X_Pulgin_FLASH_VERSION = })() : 0, - X_Pulgin_SILVER_LIGHT_ENABLED = - X_UA[ 'ActiveX' ] ? !!X_Pulgin_SILVER_LIGHT_VERSION : - X_Object_find( navigator, 'mimeTypes>application/x-silverlight>enabledPlugin' ), - //http://docs.unity3d.ru/Manual/Detecting%20the%20Unity%20Web%20Player%20using%20browser%20scripting.html X_Pulgin_UNITY_VERSION = !X_UA[ 'IE' ] || !X_UA[ 'ActiveX' ] ? @@ -56,15 +48,11 @@ var X_Pulgin_FLASH_VERSION = !X_UA[ 'IE4' ] && !X_UA[ 'IE5' ] && X_UA[ 'ActiveX' ] ? (function(){ var obj = X_Script_createActiveXObjectSafty( 'UnityWebPlayer.UnityWebPlayer.1' ); - console.log( obj && obj[ 'GetPluginVersion' ]() ); + //console.log( obj && obj[ 'GetPluginVersion' ]() ); return obj ? parseFloat( obj[ 'GetPluginVersion' ]() ) : 0; })() : 0, - X_Pulgin_UNITY_ENABLED = - X_UA[ 'ActiveX' ] ? !!X_Pulgin_UNITY_VERSION : - X_Object_find( navigator, 'mimeTypes>application/vnd.unity>enabledPlugin' ), - X_Pulgin_GEARS_ENABLED = window.GearsFactory || ( X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] ? @@ -97,21 +85,15 @@ var X_Pulgin_FLASH_VERSION = */ X[ 'Pulgin' ] = { - 'Flash' : X_Pulgin_FLASH_VERSION, + 'Flash' : X_Pulgin_FLASH_VERSION, - 'FlashEnabled' : X_Pulgin_FLASH_ENABLED, - - 'Silverlight' : X_Pulgin_SILVER_LIGHT_VERSION, - - 'SilverlightEnabled' : X_Pulgin_SILVER_LIGHT_ENABLED, - - 'Unity' : X_Pulgin_UNITY_VERSION, + 'Silverlight' : X_Pulgin_SILVER_LIGHT_VERSION, - 'UnityEnabled' : X_Pulgin_UNITY_ENABLED, + 'Unity' : X_Pulgin_UNITY_VERSION, //'QuickTime' : X_Pulgin_QUICKTIME_VERSION, - 'GearsEnabled' : !!X_Pulgin_GEARS_ENABLED + 'Gears' : !!X_Pulgin_GEARS_ENABLED }; diff --git a/0.6.x/js/05_util/02_XJSON.js b/0.6.x/js/05_util/02_XJSON.js index a99aa0e..5f86da6 100644 --- a/0.6.x/js/05_util/02_XJSON.js +++ b/0.6.x/js/05_util/02_XJSON.js @@ -50,6 +50,7 @@ function X_JSON_stringify( obj ){ * @alias X.JSON.parse */ function X_JSON_parseTrustableString( jsonString ){ + if( !jsonString ) return jsonString; // '' の場合エラ-になる return window.JSON ? JSON.parse( jsonString ) : eval( '(' + jsonString + ')' ); }; diff --git a/0.6.x/js/05_util/04_XXML.js b/0.6.x/js/05_util/04_XXML.js index 34f50a4..44a020d 100644 --- a/0.6.x/js/05_util/04_XXML.js +++ b/0.6.x/js/05_util/04_XXML.js @@ -1,7 +1,7 @@ /* * XMLWrapper_find 周りの オリジナルコードに関する情報 * Original code by pettanR team - * - http://sourceforge.jp/projects/pettanr/scm/git/clientJs/blobs/master/0.6.x/js/01_dom/18_XDomQuery.js + * - https://osdn.jp/projects/pettanr/scm/git/clientJs/blobs/master/0.6.x/js/02_dom/08_XNodeSelector.js * and * Original code by ofk ( kQuery, ksk ) * - http://d.hatena.ne.jp/ofk/comment/20090106/1231258010 @@ -23,11 +23,19 @@ function XMLWrapper( xml ){ this._rawXML = xml; }; -XMLWrapper.prototype.length = 1; -XMLWrapper.prototype[ 'has' ] = XMLWrapper_has; -XMLWrapper.prototype[ 'get' ] = XMLWrapper_get; -XMLWrapper.prototype[ 'val' ] = XMLWrapper_val; -XMLWrapper.prototype[ 'find' ] = XMLWrapper_find; +XMLWrapper.prototype.length = 1; +XMLWrapper.prototype[ 'parent' ] = XMLWrapper_parent; +XMLWrapper.prototype[ 'has' ] = XMLWrapper_has; +XMLWrapper.prototype[ 'get' ] = XMLWrapper_get; +XMLWrapper.prototype[ 'val' ] = XMLWrapper_val; +XMLWrapper.prototype[ 'find' ] = XMLWrapper_find; + +function XMLWrapper_parent(){ + if( this.length === 1 ) return this._rawXML && this._rawXML.parentNode ? new XMLWrapper( this._rawXML.parentNode ) : null; + if( this.length === 0 ) return null; + + return this[ 0 ].parentNode ? ( new XMLWrapper( this[ 0 ].parentNode ) ) : null; +}; function XMLWrapper_has( queryString ){ return !!this.find( queryString ).length; @@ -45,7 +53,7 @@ function XMLWrapper_get( index ){ }; function XMLWrapper_val( queryString, type ){ - var attr_textContent = X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ? 'innerText' : X_UA[ 'IE9' ] ? 'text' : 'textContent', + var //attr_textContent = X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ? 'innerText' : X_UA[ 'IE9' ] ? 'text' : 'textContent', wrapper, xml, v; switch( queryString ){ @@ -55,7 +63,7 @@ function XMLWrapper_val( queryString, type ){ case 'string' : case undefined : type = queryString; - queryString = undefined; + queryString = 0; }; wrapper = queryString ? this.find( queryString ) : this; @@ -83,7 +91,7 @@ function XMLWrapper_val( queryString, type ){ case 'int' : return parseFloat( v ) | 0; case 'boolean' : - return v && v !== '0' && v !== 'false' && v !== 'null' && v !== 'undefined' && v !== 'NaN'; + return !!X_String_parse( v ); //case 'string' : //default : }; @@ -469,7 +477,7 @@ function XMLWrapper_val( queryString, type ){ for( ; xnode = xmlList[ i ]; ++i ){ uid = xnode._uid; tmp = checked[ uid ]; - if( tmp === void 0 ){ + if( tmp === undefined ){ for( c = 0, node = xnode.parentNode[ pointer ], tagName = flag_all || xnode.tagName; node; node = node[ sibling ] ){ if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){ ++c; @@ -576,9 +584,10 @@ function XMLListWrapper( xmlList ){ var XMLListWrapper_0 = new XMLListWrapper(); -XMLListWrapper.prototype.length = 0; -XMLListWrapper.prototype._wraps = null; -XMLListWrapper.prototype[ 'has' ] = XMLWrapper_has; -XMLListWrapper.prototype[ 'get' ] = XMLWrapper_get; -XMLListWrapper.prototype[ 'val' ] = XMLWrapper_val; -XMLListWrapper.prototype[ 'find' ] = XMLWrapper_find; +XMLListWrapper.prototype.length = 0; +XMLListWrapper.prototype._wraps = null; +XMLListWrapper.prototype[ 'parent' ] = XMLWrapper_parent; +XMLListWrapper.prototype[ 'has' ] = XMLWrapper_has; +XMLListWrapper.prototype[ 'get' ] = XMLWrapper_get; +XMLListWrapper.prototype[ 'val' ] = XMLWrapper_val; +XMLListWrapper.prototype[ 'find' ] = XMLWrapper_find; diff --git a/0.6.x/js/06_net/01_XNetXHR.js b/0.6.x/js/06_net/01_XNetXHR.js index 411de41..0094a05 100644 --- a/0.6.x/js/06_net/01_XNetXHR.js +++ b/0.6.x/js/06_net/01_XNetXHR.js @@ -43,6 +43,17 @@ IE8 以下で xhr の失敗率が高い問題 http://tkengo-totoro.blogspot.jp/2011/11/iexmlhttprequest.html TODO クライアント側にもリトライ機構を入れてみる + +TODO xml の取得には XMLDOM を使用する +var activex = JKL.ParseXML.HTTP.ACTIVEX_XMLHTTP; // IXMLHttpRequest +if ( this.method == "GET" && ! this.textmode ) { + // use IXMLDOMElement to accept any mime types + // because overrideMimeType() is not available on IE6 + activex = JKL.ParseXML.HTTP.ACTIVEX_XMLDOM; // IXMLDOMElement +}; +// debug.print( "new ActiveXObject( '"+activex+"' )" ); +this.req = new ActiveXObject( activex ); + */ var // Opera7.6+, Safari1.2+, khtml3.?+, Gecko0.9.7+ // ie9- ではローカルリソースには MSXML を使う @@ -103,7 +114,7 @@ X[ 'XHR' ] = { * http://hakuhin.jp/as/javascript.html * Flash から JavaScript にアクセスする(3+) */ - 'FLASH' : X_Pulgin_FLASH_ENABLED && 4 <= X_Pulgin_FLASH_VERSION ? 8 : 0, + 'FLASH' : 4 <= X_Pulgin_FLASH_VERSION ? 8 : 0, 'GADGET' : 5.5 <= X_UA[ 'IE' ] || !X_UA[ 'IE' ] ? 16 : 0, @@ -424,7 +435,7 @@ if( X_XHR_w3c || X_XHR_msXML ){ headers = { 'Content-Type' : raw.contentType }; } else if( ( X_XHR._isMsXML ? 3 <= X_XHR_msXMLVer : raw.setRequestHeader ) && ( headers = raw.getAllResponseHeaders() ) ){ - headers = X_NET_XHR_parseResponseHeaders( headers ); + headers = X_XHR_parseResponseHeaders( headers ); }; // https://code.google.com/p/fakeworker-js/source/browse/src/javascript/fakeworker.js @@ -494,7 +505,45 @@ if( X_XHR_w3c || X_XHR_msXML ){ break; }; }, - + +/* + * http://www.kawa.net/works/js/jkl/parsexml.html + * +// ================================================================ +// method: documentElement() +// return: XML DOM in response body + +JKL.ParseXML.HTTP.prototype.documentElement = function() { + // debug.print( "documentElement: "+this.req ); + if ( ! this.req ) return; + if ( this.req.responseXML ) { + return this.req.responseXML.documentElement; // XMLHTTPRequest + } else { + return this.req.documentElement; // IXMLDOMDocument + } +}; + +// ================================================================ +// method: responseText() +// return: text string in response body + +JKL.ParseXML.HTTP.prototype.responseText = function() { + // debug.print( "responseText: "+this.req ); + if ( ! this.req ) return; + + // Safari and Konqueror cannot understand the encoding of text files. + if ( navigator.appVersion.match( "KHTML" ) ) { + var esc = escape( this.req.responseText ); +// debug.print( "escape: "+esc ); + if ( ! esc.match("%u") && esc.match("%") ) { + return decodeURIComponent(esc); + } + } + + return this.req.responseText; +}; + */ + onTimeout : function(){ var raw = X_XHR[ '_rawObject' ], live = !X_XHR._canceled || !X_XHR._busy; @@ -529,7 +578,7 @@ if( X_XHR_w3c || X_XHR_msXML ){ * 複数の情報が存在する場合、改行で区切られています。 */ -function X_NET_XHR_parseResponseHeaders( headerStr ){ +function X_XHR_parseResponseHeaders( headerStr ){ var headers = {}, headerPairs, i = 0, l, headerPair, index, key, val; if( !headerStr ) return headers; diff --git a/0.6.x/js/07_audio/01_XWebAudio.js b/0.6.x/js/07_audio/01_XWebAudio.js index 50cf05c..8e06469 100644 --- a/0.6.x/js/07_audio/01_XWebAudio.js +++ b/0.6.x/js/07_audio/01_XWebAudio.js @@ -78,14 +78,15 @@ if( X_Audio_constructor ){ }; -var X_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && - // TODO なんで fennec を禁止? +var X_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] && + // Android2 + Gecko で WebAudio が極めて不安定 !( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) && // Firefox40.0.5 + Windows8 で音声が途中から鳴らなくなる // Firefox41.0.1 + Windows8 で音声が途中から鳴らなくなる !( 40 <= X_UA[ 'Gecko' ] && X_UA[ 'Gecko' ] < 42 && X_UA[ 'Windows' ] ) && ( window[ 'AudioContext' ] || window[ 'webkitAudioContext' ] ), - X_WebAudio_BUFFER_LIST = [], + X_WebAudio_BUFFER_LIST = [], + X_WebAudio_need1stTouch = X_UA[ 'iOS' ], X_WebAudio, X_WebAudio_BufferLoader, X_WebAudio_fpsFix; diff --git a/0.6.x/js/07_audio/02_XHTMLAudio.js b/0.6.x/js/07_audio/02_XHTMLAudio.js index 154e4ac..61f3ddd 100644 --- a/0.6.x/js/07_audio/02_XHTMLAudio.js +++ b/0.6.x/js/07_audio/02_XHTMLAudio.js @@ -15,17 +15,36 @@ * 8. BlinkOpera32 Win8 は HTMLAudio が壊れている、WebAudio は mp3 がデコードに失敗、ogg が動作 * * memo - * 1. Android4.1 iframe 内の Audio は親に focus が移っても再生を継続する + * 1. AOSP4.1 iframe 内の Audio は親に focus が移っても再生を継続する + * 2. AOSP4.2(Genymotion) どこかのタイミングでタッチによる play を行う + * 3. AOSP oggはシークが乱れる m4a, mp3 は優秀 */ + /* + * durationFix + * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている + * + * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する + * -> 取得後に pause or 通常再生 + * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得 + * + * PC Opera12 + * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する + * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。 + * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない + * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。 + * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい?? + */ var X_HTMLAudio_playTrigger = - 6 <= X_UA[ 'iOS' ] ? 'loadeddata' : - X_UA[ 'iOS' ] < 5 ? 'stalled' : - X_UA[ 'iOS' ] ? 'suspend' : + X_UA[ 'iOS' ] < 5 ? 'stalled' : + X_UA[ 'iOS' ] < 6 ? 'suspend' : + X_UA[ 'iOS' ] < 7 ? 'suspend' : + X_UA[ 'iOS' ] ? 'loadeddata' : X_UA[ 'Safari' ] < 4 ? 'canplaythrough' : X_UA[ 'ChromeWV' ] ? 'canplaythrough' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,, ので必ず loadeddata もチェックする - X_UA[ 'AOSP' ] ? 'stalled' : + //X_UA[ 'AOSP' ] < 3 ? 'stalled' : + //X_UA[ 'AOSP' ] ? 'playing' : X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : 'loadeddata', //'canplay', X_HTMLAudio, @@ -37,7 +56,9 @@ var X_HTMLAudio_playTrigger = // Android 3.1 で ended 時に src='';src=src を実施。 X_HTMLAudio_endedFixAOSP3 = !X_HTMLAudio_endedFixAOSP2 && X_UA[ 'AOSP' ] < 4, // ended 時に play() を実施, currentTime が duration に張り付き更新されなければ src='';src=src を実施。 - X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ], + X_HTMLAudio_endedFixAOSP4 = 4 <= X_UA[ 'AOSP' ], + // ended 時に play() を実施 + X_HTMLAudio_endedFixCWV = X_UA[ 'ChromeWV' ], // Opera Mobile 12 は 2回目以降の currentTime へのセットで currentTime が更新されなくなるため、タイマーを使用する X_HTMLAudio_currentTimeFix = !!X_UA[ 'OperaMobile' ] || !!X_UA[ 'OperaTablet' ], @@ -51,25 +72,11 @@ var X_HTMLAudio_playTrigger = X_HTMLAudio_needPlayForSeek = X_UA[ 'Gecko' ], // X_HTMLAudio_pauseFix = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] && 0 < ' XP XPSP2 2003|XP64'.indexOf( X_UA[ 'Windows' ] ) ), // XP + Opera12 のみ? - /* - * durationFix - * duration が取得できるタイミングが遅くそれまでは infinity(PC Opera12), NaN(WP9), 0(Android 標準ブラウザ ChromeWebView) が入っている - * - * 1. touch が不要の場合、自動で再生を開始して duration を取得するまで再生する - * -> 取得後に pause or 通常再生 - * 2. touch が必要な場合、タッチイベント内の audio.play() で duration 取得 - * - * PC Opera12 - * 1. loadeddata 等では duration が infinity で、再生後の durationchange 時に duration が判明する - * 2. duration 判明後には currentTime によるシークと、現在時間の取得が可能になる。 - * 3. Opera12.17 Win32(XP) portable apps は勝手に再生が始まる、、、Win8+Opera では発生しない - * -> その際には timeupdate が発行されない、、、 iframe+image+audio で使わないときは破棄する、とか。 - * -> opera11、10.54 WinXP はまとも、、、 portable が怪しい?? - */ - X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), - X_HTMLAudio_durationFix = ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), + + X_HTMLAudio_need1stTouch = X_UA[ 'iOS' ] || 4 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || X_UA[ 'WinPhone' ], + X_HTMLAudio_durationFix = X_UA[ 'iOS' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'Windows' ] && 12 <= X_UA[ 'Opera' ] ) || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ), - X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇(ただしm4a, mp3は優秀, oggはシークが乱れる) + X_HTMLAudio_shortPlayFix = X_UA[ 'AOSP' ]; // Android 4.1.1 でも遭遇 @@ -88,7 +95,7 @@ if( X_Audio_constructor ){ _currentFixBegin : 0, _durationFixPhase : X_HTMLAudio_durationFix ? 1 : 0, - _durationFixSkip : X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch, + _durationFixSkip : X_HTMLAudio_durationFix && !X_HTMLAudio_need1stTouch, _lastCurrentTime : 0, _shortPlayFixON : false, @@ -96,9 +103,6 @@ if( X_Audio_constructor ){ _endedFixON : false, - _kickTimerID : false, // 処理が混み入ると AOSP で音声が再生されない - _lastKickTime : 0, - 'Constructor' : function( disatcher, source, option ){ var raw; @@ -143,13 +147,31 @@ if( X_Audio_constructor ){ 'playing', 'waiting', 'seeking', 'durationchange', 'timeupdate', 'ended' ] ); + + this[ 'listen' ]( [ + 'loadstart', 'load', + 'progress', 'error', + 'suspend', 'abort', 'emptied', 'stalled', + 'play', 'pause', 'seeked', 'ratechange', 'volumechange', + 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', + 'playing', 'waiting', 'seeking', + 'durationchange', 'timeupdate', 'ended' ], this.onDebug ); + if( X_TEMP.rawAudio ){ raw.src = source; - raw.load(); // AOSP2 で必要 + X_UA[ 'AOSP' ] < 3 && raw.load(); delete X_TEMP.rawAudio; }; }, + onDebug : function( e ){ + this.disatcher[ 'dispatch' ]( { + type : X_EVENT_DEBUG, + 'rawEvent' : e.type, + current : this.getActualCurrentTime() | 0, + duration : this.duration | 0 } ); + }, + handleEvent : function( e ){ var raw = this[ '_rawObject' ], actualEnded = e.type === 'ended', @@ -216,10 +238,10 @@ if( X_Audio_constructor ){ if( this._durationFixPhase & 3 ){ // 1 or 2 duration = raw.duration; } else - if( raw.currentTime === this._lastCurrentTime ){ + if( this.getActualCurrentTime() === this._lastCurrentTime ){ eventType = X_EVENT_MEDIA_WAITING; } else { - this._lastCurrentTime = raw.currentTime; + this._lastCurrentTime = this.getActualCurrentTime(); if( this.playing ){ end = X_Audio_getEndTime( this ) + this._shortPlayFixTime; @@ -258,8 +280,8 @@ if( X_Audio_constructor ){ if( X_HTMLAudio_volumeFix ){ raw.volume = this.gain; }; - if( X_HTMLAudio_currentTimeFix ){ - this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正 + if( X_HTMLAudio_currentTimeFix && !this._currentFixStart ){ + //this._currentFixStart = X_Timer_now(); // 正確な再生開始時間に補正 }; eventType = !this._durationFixSkip && !this._endedFixON ? X_EVENT_MEDIA_PLAYING : X_EVENT_MEDIA_WAITING; //case 'play' : // 再生が開始された。play()メソッドからの復帰後に発生する場合に発生 @@ -312,9 +334,9 @@ if( X_Audio_constructor ){ if( !( this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_CALLBACK_PREVENT_DEFAULT ) ){ this.looped = true; this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED ); - ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' ); + ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded && console.log( '☆★☆ 音声の継続用の play() @ended' ); this.actualPlay( - ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixIOS ) && actualEnded, + ( X_HTMLAudio_endedFixAOSP4 || X_HTMLAudio_endedFixCWV || X_HTMLAudio_endedFixIOS ) && actualEnded, ( X_HTMLAudio_endedFixAOSP3 || X_HTMLAudio_endedFixAOSP2 ) && actualEnded ); }; } else { @@ -325,8 +347,8 @@ if( X_Audio_constructor ){ } else if( this._loaded && this.duration && !this._ready ){ this._ready = true; - this.autoplay && X_Timer_once( 16, this, this.play ); this.disatcher[ 'asyncDispatch' ]( X_EVENT_READY ); + this.autoplay && !X_WebAudio_need1stTouch && X_Timer_once( 16, this, this.play ); console.log( '> Audio Loaded!! ' + e.type + ' d:' + ( this.duration | 0 ) ); } else if( eventType ){ @@ -382,10 +404,6 @@ if( X_Audio_constructor ){ }; raw.play(); this.playing = true; - - if( X_UA[ 'AOSP' ] && !this._kickTimerID ){ - this._kickTimerID = X_Timer_add( 100, 0, this, this._kick ); - }; } else if( X_HTMLAudio_needPlayForSeek || forcePlay ){ raw.play(); @@ -395,10 +413,9 @@ if( X_Audio_constructor ){ //http://himaxoff.blog111.fc2.com/blog-entry-97.html //Firefox3.6では一度も play() していない状態で currentTime = 0 を実行するとエラーになる。 //また、GoogleChrome7 では currentTime = 0 直後に play() すると、pause()した位置前後の音が混ざることがある。(少なくとも自分の環境では) - raw.currentTime = this._lastKickTime = this._lastCurrentTime; + raw.currentTime = this._lastCurrentTime; console.log( '[HTMLAudio] play ' + begin + ' -> ' + end + ' crt:' + ( raw.currentTime | 0 ) + ' last:' + this._lastCurrentTime ); - - // Android4.0.5 で ended イベント時に currentTime が duration に張り付いたまま変更できない + if( forceReload || ( X_HTMLAudio_endedFixAOSP4 && raw.duration && raw.currentTime === raw.duration ) ){ raw.src = ''; raw.src = this._src; @@ -408,7 +425,7 @@ if( X_Audio_constructor ){ raw.currentTime = this._lastCurrentTime; this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); X_HTMLAudio_endedFixAOSP2 && raw.load(); - }; + }; }; if( X_HTMLAudio_currentTimeFix ){ @@ -417,20 +434,6 @@ if( X_Audio_constructor ){ }; }, - _kick : function(){ - var raw = this[ '_rawObject' ]; - - console.log( ' >> KICK ? ct:' + ( raw.currentTime ) + ' lkt:' + this._lastKickTime ); - if( this.playing && raw.currentTime === this._lastKickTime ){ - this.disatcher[ 'dispatch' ]( X_EVENT_MEDIA_WAITING ); - console.log( ' >> KICK !!' ); - raw.play(); - } else { - delete this._kickTimerID; - return X_CALLBACK_UN_LISTEN; - }; - }, - actualPause : function(){ console.log( '[HTMLAudio] pause' ); diff --git a/0.6.x/js/07_audio/10_XAudioSprite.js b/0.6.x/js/07_audio/10_XAudioSprite.js index 23ff7e5..cd91dc4 100644 --- a/0.6.x/js/07_audio/10_XAudioSprite.js +++ b/0.6.x/js/07_audio/10_XAudioSprite.js @@ -8,10 +8,8 @@ var X_AudioSprite_shouldUse = X_HTMLAudio && ( X_UA[ 'iOS' ] || X_UA[ 'AO X_AudioSprite_useVideoForMulti = //( 3.1 <= X_UA[ 'AOSP' ] < 4 ) || //( ( 4.2 <= X_UA[ 'AOSP' ] ), // ドスパラパッドはビデオのインライン再生が不可 - false, //X_UA[ 'ChromeWV' ], - X_AudioSprite_needTouchAndroid = X_UA[ 'ChromeWV' ] && !X_WebAudio, - X_AudioSprite_needTouchFirst = X_HTMLAudio_need1stTouch, - X_AudioSprite_disableMultiTrack = ( X_UA[ 'iOS' ] && !X_WebAudio_context ) || ( X_UA[ 'AOSP4' ] ) || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE' ] < 12 ), + false, + X_AudioSprite_disableMultiTrack = !X_WebAudio && ( X_UA[ 'iOS' ] || 4 <= X_UA[ 'AOSP' ] || X_UA[ 'ChromeWV' ] || ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ) ), X_AudioSprite_enableVolume = X_HTMLAudio && ( !X_UA[ 'iOS' ] && !X_UA[ 'AOSP' ] && !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] ), // TODO fennec は 25以上 // http://tukumemo.com/html5-audio-sp/ // iOS6、Android4.1から同時再生が可能になりました。 @@ -110,7 +108,6 @@ X[ 'AudioSprite' ] = function( setting ){ }; X[ 'AudioSprite' ][ 'shouldUse' ] = X_AudioSprite_shouldUse; -X[ 'AudioSprite' ][ 'needTouchFirst' ] = X_AudioSprite_needTouchFirst; X[ 'AudioSprite' ][ 'enableMultiTrack' ] = !X_AudioSprite_disableMultiTrack; // 再生が終わっているもの、終わりかけのものを探す @@ -146,22 +143,14 @@ X_AudioSprite_members = /** * モバイル用タッチイベント中に呼び出す */ - 'load' : function(){ - var tracks = X_AudioSprite_TEMP.tracks, - i = 0, l = tracks.length; + 'on1stTouch' : function(){ + var i = 0, l = X_AudioSprite_TEMP.tracks.length; for( ; i < l; ++i ){ - if( X_AudioSprite_needTouchAndroid ){ - console.log( '[duration fix]開始 - ' + tracks[ i ][ '_rawObject' ].duration ); - tracks[ i ]._durationFixPhase = 1; - tracks[ i ][ '_rawObject' ].play(); - } else - if( X_UA[ 'WinPhone' ] ){ - console.log( 'WinPhone : touch -> play()' ); - //tracks[ i ].play( 0, X_AudioSprite_lengthSilence, true, 0, X_AudioSprite_lengthSilence ).seek( 0 ); - this[ 'pause' ]( i ); + if( X_UA[ 'iOS' ] ){ + X_AudioSprite_TEMP.tracks[ i ][ '_rawObject' ].load(); } else { - tracks[ i ][ '_rawObject' ].load(); + X_AudioSprite_instance[ 'pause' ]( i ); }; }; }, @@ -199,7 +188,7 @@ X_AudioSprite_members = track = X_AudioSprite_TEMP.bgmTrack = tracks[ 0 ]; }; - if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_AudioSprite_handleEvent ).playing ){ + if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], X_AudioSprite_instance, X_AudioSprite_handleEvent ).playing ){ track.setState({ 'loop' : true, 'looped' : X_AudioSprite_TEMP.bgmLooped, @@ -219,7 +208,7 @@ X_AudioSprite_members = if( 1 < tracks.length ){ track = X_AudioSprite_getTrackEnded( X_AudioSprite_TEMP.bgmPlaying ); track - [ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_AudioSprite_handleEvent ) + [ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], X_AudioSprite_instance, X_AudioSprite_handleEvent ) .setState( { 'looped' : false } ); track.play( preset[ 0 ], preset[ 1 ], true, 0, X_AudioSprite_lengthSilence ); } else { @@ -231,7 +220,7 @@ X_AudioSprite_members = }; track = tracks[ 0 ]; - if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_AudioSprite_handleEvent ).playing ){ + if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_WAITING, X_EVENT_MEDIA_SEEKING, X_EVENT_MEDIA_BEFORE_LOOP ], X_AudioSprite_instance, X_AudioSprite_handleEvent ).playing ){ track.setState({ 'loop' : true, 'looped' : false, @@ -267,8 +256,8 @@ X_AudioSprite_members = }; track && track.play( 0, X_AudioSprite_lengthSilence, true, 0, X_AudioSprite_lengthSilence ); track && track.seek( 0 ); - this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PAUSED ); - return this; + X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_MEDIA_PAUSED ); + return X_AudioSprite_instance; }, /** @@ -286,7 +275,7 @@ X_AudioSprite_members = start = X_Audio_getStartTime( track, end ); 0 <= position && position <= ( end - start ) && track.seek( start + position ); }; - return this; + return X_AudioSprite_instance; }, /** @@ -305,14 +294,14 @@ X_AudioSprite_members = for( i = X_AudioSprite_TEMP.tracks.length; i; ){ X_AudioSprite_TEMP.tracks[ --i ].volume( opt_volume ); }; - return this; + return X_AudioSprite_instance; }; track = X_AudioSprite_TEMP.tracks[ uid ]; if( opt_volume === undefined ){ return track ? track.gain : -1; }; track && track.volume( opt_volume ); - return this; + return X_AudioSprite_instance; }, /** @@ -340,12 +329,12 @@ X_AudioSprite_members = return { 'volume' : X_AudioSprite_TEMP.volume, 'playing' : false }; }; track && track.setState( opt_obj ); - return this; + return X_AudioSprite_instance; } }; function X_AudioSprite_backendHandler( e ){ - var i, backend, option, src, name, last, _e; + var i, backend, option, src, name, last, _e, touch; switch( e.type ){ case X_EVENT_BACKEND_READY : @@ -353,9 +342,9 @@ function X_AudioSprite_backendHandler( e ){ backend = X_Audio_BACKENDS[ e[ 'backendID' ] ]; option = e[ 'option' ]; - this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_AudioSprite_backendHandler ); - this[ 'source' ] = src = e[ 'source' ]; - this[ 'backendName' ] = name = backend.backendName; + X_AudioSprite_instance[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_AudioSprite_backendHandler ); + X_AudioSprite_instance[ 'source' ] = src = e[ 'source' ]; + X_AudioSprite_instance[ 'backendName' ] = name = backend.backendName; //console.log( i + ' / ' + X_AudioSprite_numTracks ); @@ -365,11 +354,7 @@ function X_AudioSprite_backendHandler( e ){ console.log( 'use video' ); }; // Audiobackend の owner として null を渡すとAudioBackend 自身へ dispatch する - X_AudioSprite_TEMP.tracks.push( last = backend.klass( null, e[ 'source' ], option ) ); - - - //console.dir( backend ); - //console.dir( last ); + X_AudioSprite_TEMP.tracks.push( last = backend.klass( null, e[ 'source' ], option )[ 'listen' ]( X_EVENT_DEBUG, X_AudioSprite_instance, X_AudioSprite_handleEvent ) ); }; _e = { @@ -377,45 +362,39 @@ function X_AudioSprite_backendHandler( e ){ 'source' : src, 'backendName' : name }; + // touch 可能で backend ready - if( X_AudioSprite_needTouchFirst ){ - if( name === 'WebAudio' ){ - _e[ 'needTouchForPlay' ] = true; - } else { - _e[ 'needTouchForLoad' ] = true; - }; + if( name === 'WebAudio' ){ + _e[ 'needTouchForPlay' ] = touch = X_WebAudio_need1stTouch; + } else + if( name === 'HTMLAudio' ){ + _e[ 'needTouchForLoad' ] = touch = X_HTMLAudio_need1stTouch; }; - this[ 'asyncDispatch' ]( _e ); + + X_AudioSprite_instance[ 'asyncDispatch' ]( _e ); console.log( 'AudioSprite - X_EVENT_BACKEND_READY' ); - last[ 'listenOnce' ]( X_EVENT_READY, this, X_AudioSprite_backendHandler ); + last[ 'listenOnce' ]( touch && !X_HTMLAudio_durationFix ? X_EVENT_MEDIA_PLAYING : X_EVENT_READY, X_AudioSprite_instance, X_AudioSprite_backendHandler ); // READY, needTouchForPlay, needTouchForLoad - if( X_HTMLAudio_durationFix && !X_AudioSprite_needTouchFirst ){ + if( /* X_HTMLAudio_durationFix && */ !touch ){ for( i = 0; i < X_AudioSprite_TEMP.tracks.length; ++i ){ - this[ 'pause' ]( i ); + X_AudioSprite_instance[ 'pause' ]( i ); }; }; return X_CALLBACK_STOP_NOW; case X_EVENT_BACKEND_NONE : - this[ 'unlisten' ]( X_EVENT_BACKEND_READY, this, X_AudioSprite_backendHandler ) + X_AudioSprite_instance[ 'unlisten' ]( X_EVENT_BACKEND_READY, X_AudioSprite_instance, X_AudioSprite_backendHandler ) [ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE ); return X_CALLBACK_STOP_NOW; - case X_EVENT_READY : - if( X_AudioSprite_needTouchAndroid ){ - for( i = 0; i < X_AudioSprite_TEMP.tracks.length; ++i ){ - this[ 'pause' ]( i ); - }; - e.target[ 'listenOnce' ]( X_EVENT_MEDIA_PLAYING, this, this[ 'asyncDispatch' ], [ X_EVENT_READY ] ); - return; - }; - + case X_EVENT_READY : + case X_EVENT_MEDIA_PLAYING : console.log( 'X.AudioSprite - Ready!' ); - this[ 'asyncDispatch' ]( X_EVENT_READY ); + X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_READY ); break; }; }; @@ -426,22 +405,22 @@ function X_AudioSprite_handleEvent( e ){ switch( e.type ){ case X_EVENT_MEDIA_PLAYING : - ( e.target === X_AudioSprite_TEMP.bgmTrack || !e.target.looped ) && this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING ); + ( e.target === X_AudioSprite_TEMP.bgmTrack || !e.target.looped ) && X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING ); break; case X_EVENT_MEDIA_WAITING : case X_EVENT_MEDIA_SEEKING : - ( e.target === X_AudioSprite_TEMP.bgmTrack || !e.target.looped ) && this[ 'asyncDispatch' ]( e.type ); + ( e.target === X_AudioSprite_TEMP.bgmTrack || !e.target.looped ) && X_AudioSprite_instance[ 'asyncDispatch' ]( e.type ); break; case X_EVENT_MEDIA_BEFORE_LOOP : if( e.target === X_AudioSprite_TEMP.bgmTrack ){ X_AudioSprite_TEMP.bgmLooped = true; - this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid + X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid } else { if( e.target.looped ){ - //this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid + // X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid } else { - this[ 'asyncDispatch' ]( X_EVENT_MEDIA_ENDED ); // TODO uid + X_AudioSprite_instance[ 'asyncDispatch' ]( X_EVENT_MEDIA_ENDED ); // TODO uid }; //console.log( '[AudioSprite] bgmPlaying:' + X_AudioSprite_TEMP.bgmPlaying + ' ' + !X_AudioSprite_TEMP.bgmTrack ); @@ -449,12 +428,21 @@ function X_AudioSprite_handleEvent( e ){ // single track | iOS if( X_AudioSprite_TEMP.bgmPlaying && !X_AudioSprite_TEMP.bgmTrack ){ X_AudioSprite_TEMP.bgmTrack = e.target; - this.play( X_AudioSprite_TEMP.bgmName ); + X_AudioSprite_instance.play( X_AudioSprite_TEMP.bgmName ); return X_CALLBACK_PREVENT_DEFAULT; }; }; break; + + case X_EVENT_DEBUG : + i = X_AudioSprite_TEMP.tracks.indexOf( e.target ); + if( 0 <= i ){ + e[ 'trackID' ] = i; + X_AudioSprite_instance[ 'dispatch' ]( e ); + }; + break; + // TODO Android Firefox で アクティブ検出できない! case X_EVENT_VIEW_ACTIVATE : console.log( '■ アクティブ' ); @@ -493,7 +481,7 @@ function X_AudioSprite_handleEvent( e ){ X_AudioSprite_TEMP.bgmLooped = false; X_AudioSprite_TEMP.bgmPlaying = false; - X_ViewPort[ 'unlisten' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], this, X_AudioSprite_handleEvent ); + X_ViewPort[ 'unlisten' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_AudioSprite_instance, X_AudioSprite_handleEvent ); break; }; }; -- 2.11.0