-var\r
/**\r
* Array か?判定する。argumnets 等のフェイク Array は false なので注意。\r
- * @funciton\r
- * @alias X.Type._isArray\r
+ * @function\r
+ * @alias X.Type.isArray\r
*/\r
- X_Type_isArray =\r
+var X_Type_isArray =\r
new Function( 'v',\r
X_UA[ 'IE' ] < 5.5 || X_UA[ 'NetFront' ] < 4 ? // netfront3.4 は html に instanceof をすると error になる\r
'return v&&v.push===Array.prototype.push' : // win ie5-, MacIE5.2\r
\r
/**\r
* HTMLElement か?判定する。ちなみに return v instanceof Element は ie8 でエラー。\r
- * @funciton\r
+ * @function\r
* @alias X.Type.isHTMLElement\r
*/\r
X_Type_isHTMLElement =\r
new Function( 'v',\r
( X_UA[ 'IE4' ] || X_UA[ 'MacIE' ] ) ?\r
- 'return v&&v.tagName&&v.insertAdjacentHTML&&true' : // ie4 or MacIE5.23, v.all <- error\r
+ 'return v&&v.tagName&&v.insertAdjacentHTML&&!0' : // ie4 or MacIE5.23, v.all <- error\r
X_UA[ 'NetFront' ] < 4 ?\r
'return v&&v.nodeType===1' : // instanceof not a function. netfront3.4 は html に instanceof をすると error になる\r
window[ 'HTMLElement' ] ?\r
\r
\r
/**\r
- * http://pettanr.sourceforge.jp/test/type.html\r
- * ビルトイン方の判定に使用する関数を集めたもの。ブラウザのネイティブな判定関数には不可解な挙動があるので、X.Type を使用するほうがよい。\r
- * \r
+ * <p>ビルトイン型の判定に使用する関数を集めたもの。ブラウザのネイティブな判定関数には不可解な挙動があるので、X.Type を使用するほうがよい。\r
+ * <a href="http://pettanr.sourceforge.jp/test/type.html">http://pettanr.sourceforge.jp/test/type.html</a>\r
* @namespace X.Type\r
* @alias X.Type\r
*/\r
};\r
\r
/**\r
- * Object か?判定する。typeof null === 'object' に対策済なので null は Object ではない。\r
- * new String(), new Number(), new Boolean() も typeof object なので対策\r
+ * <p>Object か?判定する。\r
+ * <p>typeof null === 'object' に対策済なので null は Object ではない。\r
+ * <p>new String(), new Number(), new Boolean() も typeof object なので対策\r
* @alias X.Type.isObject\r
*/\r
function X_Type_isObject( v ){\r
- return v && typeof v === 'object'; // && ( v !== v + '' && v !== v + 0 && v !== true ) ; // typeof null === 'object' に対策\r
+ return v && typeof v === 'object' && v !== v + '' && v !== v + 0 && v !== true; // typeof null === 'object' に対策\r
};\r
/**\r
* Function か?判定する。\r
* @alias X.Type.isString\r
*/\r
function X_Type_isString( v ){\r
- return typeof v === 'string'; // v === v + ''; // 文字列の加算は IE で遅いかも。\r
+ return /* typeof v === 'string'; */ v === v + ''; // 文字列の加算は IE で遅いかも。\r
};\r
/**\r
- * 数値値か?判定する。\r
+ * 数値か?判定する。\r
* @alias X.Type.isNumber\r
*/\r
function X_Type_isNumber( v ){\r
- return typeof v === 'number'; // v !== v || v + 0 === v;\r
+ return /* typeof v === 'number'; */ v + 0 === v || v !== v;\r
};\r
/**\r
* finite か?判定する。isFinite( '123' ) === true に対策済。\r
*/ \r
function X_Type_isImage( v ){\r
if( v && v.constructor === window.Image ) return true;\r
- if( v && window.HTMLImageElement && v.constructor === window.HTMLImageElement ) return true; // ie6- は constructor が undef、HTMLImageElement が undef なので、HTMLElement の存在確認が必要\r
+ if( v && window.HTMLImageElement && v.constructor === window.HTMLImageElement ) return true; // ie6- は constructor が undef、HTMLImageElement が undef なので、HTMLElement の存在確認が必要\r
if( X_UA[ 'WebKit' ] < 525.13 ){ // Safari3-\r
if( v && v.src !== undefined && v.onload !== undefined && X_Type_isNumber( v.height ) && X_Type_isNumber( v.width ) && X_Type_isBoolean( v.complete ) ){\r
return true;\r
* @alias X.Type.isUndefined\r
*/\r
function X_Type_isUndefined( v ){\r
- return v === void 0;\r
+ return v === undefined;\r
};\r
\r
console.log( 'X.Core.Type' );\r
pairStore = X_Pair_PAIR_STORE_LIST[ X_Pair_PAIR_STORE_LIST.length - 1 ];\r
\r
X_Pair_noChashe = true;\r
- if( X_Pair_get( key ) ) return;\r
+ if( X_Pair_get( key ) || !( X_Type_isObject( key ) || X_Type_isArray( key ) || X_Type_isFunction( key ) ) ) return;\r
\r
if( keyStore.length === X_Pair_SIZE ){\r
keyStore = X_Pair_KEY_STORE_LIST[ X_Pair_KEY_STORE_LIST.length ] = [];\r
\r
X_Class_CLASS_LIST = [],\r
X_Class_DEF_LIST = [],\r
- X_Class_PRIVATE_CLASS_LIST = [],\r
- X_Class_PRIVATE_DEF_LIST = [],\r
X_Class_CALLING_SUPER = [],\r
X_Class_CALL_SUPER_STACK = [],\r
- X_Class_killPrivateFlag = false,\r
X_Class_traits = null,\r
X_Class_useObjectCreate = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf\r
X_Class_use_proto_ = !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] && !!X_emptyFunction.prototype.__proto__,\r
klass = X_Class_getClass( instance ),\r
def = X_Class_getClassDef( klass ),\r
data, p, i;\r
- if( def.isPrivate && !X_Class_killPrivateFlag && ( !this[ '_listeners' ] || !this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] ) ){\r
- X.Logger.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );\r
- return;\r
- };\r
- X_Class_killPrivateFlag = false; // instance.kill() 内で PrivateInstance.kill() を防ぐため\r
\r
// TODO kill 中の kill の呼び出しを防ぐ, 破棄済のインスタンスへの kill\r
\r
if( this[ 'instanceOf' ]( X_EventDispatcher ) ){\r
- if( !def.isPrivate ){\r
- if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){\r
- this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );\r
- return;\r
- };\r
- if( this[ '_listeners' ] && this[ '_listeners' ][ X_Listeners_.DISPATCHING ] ){\r
- this[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;\r
- return;\r
- };\r
- } else {\r
- this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ); \r
+\r
+ if( this[ 'dispatch' ]( X_EVENT_BEFORE_KILL_INSTANCE ) & X_Callback_PREVENT_DEFAULT ){\r
+ this[ 'dispatch' ]( X_EVENT_KILL_INSTANCE_CANCELED );\r
+ return;\r
+ };\r
+ if( this[ '_listeners' ] && this[ '_listeners' ][ X_LISTENERS_DISPATCHING ] ){\r
+ this[ '_listeners' ][ X_LISTENERS_KILL_RESERVED ] = true;\r
+ return;\r
};\r
\r
// asyncDispatch の削除\r
def.live && def.live.splice( def.live.indexOf( instance ), 1 );\r
def.pool[ def.pool.length ] = instance;\r
};\r
- if( def.privateClass ){\r
- i = def.userList.indexOf( instance );\r
- if( i !== -1 ){\r
- data = X_Class_getPrivate( instance );\r
- if( data[ '_listeners' ] && data[ '_listeners' ][ X_Listeners_.DISPATCHING ] && data[ 'instanceOf' ]( X.EventDispatcher ) ){\r
- data[ '_listeners' ][ X_Listeners_.KILL_RESERVED ] = true;\r
- } else {\r
- X_Class_killPrivateFlag = true;\r
- data[ 'kill' ]();\r
- };\r
- def.dataList.splice( i, 1 );\r
- def.userList.splice( i, 1 );\r
- };\r
- };\r
},\r
\r
/**\r
POOL_OBJECT : 1,\r
ABSTRACT : 2,\r
FINAL : 4,\r
- SUPER_ACCESS : 8,\r
- PRIVATE_DATA : 16,\r
- SINGLETON : 32\r
+ SINGLETON : 8\r
};\r
\r
/**\r
* </ol>\r
* \r
* <ol>\r
- * <li>X_Class.create( opt_settings, opt_name, opt_privateClass, opt_props ) でクラスを登録.\r
+ * <li>X.Class.create( opt_settings, opt_name, opt_props ) でクラスを登録.\r
* <li>コンストラクタ となるメソッドは、opt_props 内の Constructor : function( arg ){ ... }, に書く.\r
* <li>通常通り new で インスタンス生成\r
* <li>kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.\r
* @namespace X.Class\r
* @alias X.Class\r
*/ \r
-X[ 'Class' ] = {\r
+X[ 'Class' ] = /** @lends X.Class */ {\r
\r
/**\r
* 設定なし。\r
'FINAL' : X_Class.FINAL,\r
\r
/**\r
- * 使用を中止。petanR ライブラリ使用プロジェクトから SUPER_ACCESS を消したらここも削除。\r
- * @const\r
- */\r
- 'SUPER_ACCESS' : X_Class.SUPER_ACCESS,\r
-\r
- /**\r
- * 内部コード、主に X.UI フレームワークに対して、フレーム外に露出するインスタンスとペアで動作する、シャドウなインスタンスの使用を宣言する。\r
- * Javascript はインスタンス毎のカプセル化がとてもコスト高。微妙なコスト増で隠蔽されたインスタンスを使う。\r
- * @const\r
- */\r
- 'PRIVATE_DATA' : X_Class.PRIVATE_DATA,\r
-\r
- /**\r
* 未実装。でも目印になるので付けておきましょう。\r
* @const\r
*/\r
'SINGLETON' : X_Class.SINGLETON,\r
\r
- 'create' : X_Class_create,\r
+ 'create' : X_Class_create\r
\r
// TODO collect\r
- \r
- '_newPrivate' : X_Class_newPrivate,\r
- \r
- '_getPrivate' : X_Class_getPrivate\r
- \r
};\r
\r
\r
// ------------------------------------------------------------------------- //\r
/**\r
* クラスを定義する。<br>\r
- * X_Class.create() によるクラス定義は必ずしもコンストラクタを必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。\r
- * @alias X_Class.create\r
+ * X.Class.create() によるクラス定義は必ずしもコンストラクタ('Constructor')を必要としません。クラス定義時にコンストラクタが未設定の場合、スーパークラスがあればそのコンストラクタを使用します。\r
+ * @alias X.Class.create\r
* @param {string} [displayName] クラスの名前\r
* @param {number} [classSetting=0] X_Class.POOL_OBJECT | X_Class.FINAL など\r
- * @param {__ClassBase__=} [privateClass] このクラスとペアで動作するシャドウクラス\r
* @param {object} [props={}] このクラスのメンバと関数。コンストラクタは Constructor と書くこと\r
* @return {__ClassBase__}\r
+ * @example var myClass = X.Class.create(\r
+ * 'myClass',\r
+ * X.Class.FINAL,\r
+ * {\r
+ * name : '',\r
+ * Constructor : function( obj ){\r
+ * this.name = obj.name;\r
+ * },\r
+ * getName : function(){\r
+ * return this.name;\r
+ * },\r
+ * setName : function(v){\r
+ * this.name = v;\r
+ * }\r
+ * }\r
+ * );\r
*/\r
function X_Class_create( /* displayName, classSetting, privateClass, props */ ){\r
var args = X_Object_cloneArray( arguments ),\r
displayName = args[ 0 ],\r
classSetting,\r
- opt_pool, opt_abstract, opt_final, opt_private,\r
+ opt_pool, opt_abstract, opt_final,\r
privateDef,\r
props,\r
klass,\r
opt_pool = !!( classSetting & X_Class.POOL_OBJECT );\r
opt_abstract = !!( classSetting & X_Class.ABSTRACT );\r
opt_final = !!( classSetting & X_Class.FINAL );\r
- opt_private = !!( classSetting & X_Class.PRIVATE_DATA );\r
if( opt_final && opt_abstract ){\r
X.Logger.critical( 'final & Abstract!' );\r
return;\r
classDef.setting = 0;\r
};\r
\r
- // シャドウクラス\r
- if( X_Class_PRIVATE_CLASS_LIST.indexOf( args[ 0 ] ) !== -1 ){\r
- privateDef = X_Class_getClassDef( args[ 0 ] );\r
- if( privateDef.isPrivate !== true ){\r
- X.Logger.critical( 'PrivateClass not found! please, X_Class.create( X_Class.PRIVATE, {...} ).' );\r
- return;\r
- } else\r
- if( privateDef.Abstract === true ){\r
- X.Logger.critical( 'PrivateClass is Abstract!' );\r
- return;\r
- };\r
- classDef.privateClass = args.shift();\r
- };\r
- \r
// インスタンスのメンバー\r
props = args[ 0 ];\r
if( !X_Type_isObject( props ) ){\r
} else\r
if( opt_pool ){\r
classDef.pool = [];\r
- if( opt_private === false ) classDef.live = [];\r
+ classDef.live = [];\r
}; \r
if( opt_final ){\r
classDef.Final = true;\r
} else {\r
klass[ 'inherits' ] = X_Class_inherits;\r
}; \r
- if( opt_private ){\r
- if( classDef.privateClass ){\r
- X.Logger.critical( 'Private Data Class has no PrivateClass!' );\r
- return;\r
- };\r
- classDef.isPrivate = true;\r
- X_Class_PRIVATE_CLASS_LIST.push( klass );\r
- X_Class_PRIVATE_DEF_LIST.push( classDef );\r
- } else {\r
- X_Class_CLASS_LIST.push( klass );\r
- X_Class_DEF_LIST.push( classDef ); \r
- };\r
+ \r
+ X_Class_CLASS_LIST.push( klass );\r
+ X_Class_DEF_LIST.push( classDef ); \r
+\r
return klass;\r
};\r
\r
klass = cList[ --i ];\r
if( instance.constructor === klass ) return klass;\r
};\r
- cList = X_Class_PRIVATE_CLASS_LIST;\r
- i = cList.length;\r
- for( ; i; ){\r
- klass = cList[ --i ];\r
- if( instance.constructor === klass ) return klass;\r
- };\r
- \r
if( cList.indexOf( instance ) !== -1 ) return instance;\r
- if( X_Class_CLASS_LIST.indexOf( instance ) !== -1 ) return instance;\r
};\r
\r
function X_Class_getClassDef( KlassOrInstance ){\r
if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
if( i !== -1 ) return X_Class_DEF_LIST[ i ];\r
\r
- i = X_Class_PRIVATE_CLASS_LIST.indexOf( KlassOrInstance );\r
- if( i === -1 ) i = X_Class_PRIVATE_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
- if( i !== -1 ) return X_Class_PRIVATE_DEF_LIST[ i ];\r
- \r
if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
- if( X_Class_PRIVATE_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
-};\r
-\r
-function X_Class_newPrivate( /* instance, args */ ){\r
- var args = X_Object_cloneArray( arguments ),\r
- user = args.shift(),\r
- def = X_Class_getClassDef( user ),\r
- privateClass = def.privateClass,\r
- privateDef = X_Class_getClassDef( privateClass ),\r
- i = -1;\r
- if( def.userList ){\r
- i = def.userList.indexOf( user );\r
- } else {\r
- def.userList = [];\r
- def.dataList = [];\r
- };\r
- if( i !== -1 ){\r
- X.Logger.critical( 'PrivateData already exist!' );\r
- return;\r
- };\r
- if( privateDef._tempUser ){\r
- X.Logger.critical( 'newPrivate を連続呼び出しされたところ破綻' );\r
- return;\r
- };\r
- privateDef._tempUser = user;\r
- return X_Class_actualConstructor( privateClass( X_Closure_COMMAND_BACK ), args );// privateClass.__new( args );\r
-};\r
-\r
-function X_Class_getPrivate( instance ){\r
- var def = X_Class_getClassDef( instance ),\r
- i = def.userList.indexOf( instance );\r
- if( i !== -1 ) return def.dataList[ i ];\r
};\r
\r
/* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */\r
// クラス設定がない場合、親からコピーして、Abstract flag は落とす??\r
classSetting = superDef.setting;// &= ~X_Class.ABSTRACT;\r
};\r
- if( superDef.isPrivate ) classSetting = classSetting | X_Class.PRIVATE_DATA;\r
\r
params.push( classSetting );\r
\r
// サブクラスのシャドウ\r
if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){\r
params.push( args.shift() );\r
- } else\r
- if( superDef.privateClass ){\r
- params.push( superDef.privateClass );\r
};\r
\r
/* props 未定義でも可 */\r
dataUser = def._tempUser,\r
instance, obj,\r
userDef;\r
+\r
if( def.Abstract ){\r
X.Logger.critical( 'AbstractClass!' );\r
return;\r
};\r
- if( def.isPrivate && !dataUser ){\r
- X.Logger.critical( 'use myClass.newPrivate( instance, ...args )!' );\r
- return;\r
- };\r
\r
instance = def.pool && def.pool.length > 0 ?\r
def.pool.pop() :\r
Object.create( klass.prototype ) :\r
new klass( X_Closure_COMMAND_DROP );\r
\r
- if( def.isPrivate ){\r
- userDef = X_Class_getClassDef( dataUser );\r
- userDef.dataList.push( instance );\r
- userDef.userList.push( dataUser );\r
- instance.User = dataUser;\r
- def._tempUser = null;\r
- } else {\r
- def.live && def.live.push( instance );\r
- };\r
+ def.live && def.live.push( instance );\r
\r
obj = def.Constructor ?\r
def.Constructor.apply( instance, args ) :\r
def.SuperConstructor &&\r
def.SuperConstructor.apply( instance, args );\r
- if( ( X_Type_isObject( obj ) && obj !== instance ) || X_Type_isFunction( obj ) ){ // Class\r
+\r
+ if( obj !== instance && ( X_Type_isObject( obj ) || X_Type_isFunction( obj ) ) ){ // Class\r
instance[ 'kill' ]();\r
return obj;\r
};\r
});\r
\r
var // 内部イベント\r
- X_EVENT_PRE_INIT = 5, // X_Listeners_.KILL_RESERVED に +1 した値から開始。\r
+ X_EVENT_PRE_INIT = 5, // X_LISTENERS_KILL_RESERVED に +1 した値から開始。\r
X_EVENT_XTREE_READY = 6,\r
X_EVENT_INIT = 7,\r
\r
X_EVENT_MEDIA_WAITING = 47,\r
X_EVENT_MEDIA_SEEKING = 48,\r
\r
- X_Event_last = 48;\r
+ X_EVENT_NEED_AUTH = 49,\r
+ \r
+ X_Event_last = 49;\r
\r
/**\r
* フレームワーク内で定義されたイベント。\r
'MEDIA_PAUSED' : X_EVENT_MEDIA_PAUSED,\r
'MEDIA_ENDED' : X_EVENT_MEDIA_ENDED,\r
'MEDIA_WAITING' : X_EVENT_MEDIA_WAITING,\r
- 'MEDIA_SEEKING' : X_EVENT_MEDIA_SEEKING\r
+ 'MEDIA_SEEKING' : X_EVENT_MEDIA_SEEKING,\r
+ \r
+ 'NEED_AUTH' : X_EVENT_NEED_AUTH\r
};\r
\r
X_TEMP.onSystemReady.push(\r
* \r
* <dl>\r
* <dt>0:ACTUAL_HANDLER\r
- * <dd>ã\82¤ã\83\99ã\83³ã\83\88ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\81® addEventListener ç\89ã\81«æ¸¡ã\81\95ã\82\8cã\82\8bå®\9fé\9a\9bã\81®é\96¢æ\95°(å\86\8då\88©ç\94¨å\8f¯è\83½ã\82¯ã\83ã\83¼ã\82¸ã\83£)を控えています。\r
+ * <dd>ã\82¤ã\83\99ã\83³ã\83\88ã\82¿ã\83¼ã\82²ã\83\83ã\83\88ã\81® addEventListener ç\89ã\81«æ¸¡ã\81\95ã\82\8cã\82\8bå®\9fé\9a\9bã\81®é\96¢æ\95°(å¤\9aã\81\8fã\81®å ´å\90\88ã\80\81å\86\8då\88©ç\94¨å\8f¯è\83½ã\82¯ã\83ã\83¼ã\82¸ã\83£ã\80\81ã\81\9dã\82\8c以å¤\96ã\81¯é\80\9a常ã\81®é\96¢æ\95°)を控えています。\r
* <dt>1:DISPATCHING number\r
* <dd>dispatch 中か?さらにインスタンス自身の dispatch がネストした場合、その深さを記憶します。\r
* <dt>2:RESERVES Array\r
* <dd>dispatch 中に kill() が呼ばれた場合に一旦 kill をキャンセルし、完了時(DISPATCHING===0)に再度 kill() するためのフラグです。\r
* </dl>\r
* \r
- * @class __X_EventDispatcher_Listeners__\r
+ * @class __Listeners__\r
* @private\r
* @abstract\r
*/\r
-var\r
- /** @enum {number} */\r
- X_Listeners_ =\r
- /** @lends __X_EventDispatcher_Listeners__ */\r
- {\r
- ACTUAL_HANDLER : 0,\r
- DISPATCHING : 1,\r
- RESERVES : 2,\r
- UNLISTENS : 3,\r
- KILL_RESERVED : 4 // X.Event で、イベントIDを 5 から始めているので注意。\r
- };\r
+var X_Listeners_;\r
+\r
+var /** @const */\r
+ X_LISTENERS_ACTUAL_HANDLER = 0,\r
+ /** @const */\r
+ X_LISTENERS_DISPATCHING = 1,\r
+ /** @const */\r
+ X_LISTENERS_RESERVES = 2,\r
+ /** @const */\r
+ X_LISTENERS_UNLISTENS = 3,\r
+ /** @const */\r
+ X_LISTENERS_KILL_RESERVED = 4; // X.Event で、イベントIDを 5 から始めているので注意。\r
\r
\r
\r
cbHash = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
};\r
\r
- if( ( unlistens = listeners[ X_Listeners_.UNLISTENS ] ) && ( unlistens = unlistens[ opt_type ] ) ){\r
+ if( ( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ) && ( unlistens = unlistens[ opt_type ] ) ){\r
for( i = unlistens.length; i; ){\r
f = unlistens[ --i ];\r
if( f === cbHash || ( f.context === cbHash.context && f.func === cbHash.func && f.supplement === cbHash.supplement && f.lock === lock ) ) return false;\r
e[ 'target' ] = e[ 'target' ] || this;\r
e[ 'currentTarget' ] = e[ 'currentTarget' ] || this;\r
\r
- if( listeners[ X_Listeners_.DISPATCHING ] ){\r
- ++listeners[ X_Listeners_.DISPATCHING ];\r
+ if( listeners[ X_LISTENERS_DISPATCHING ] ){\r
+ ++listeners[ X_LISTENERS_DISPATCHING ];\r
} else {\r
- listeners[ X_Listeners_.DISPATCHING ] = 1;\r
+ listeners[ X_LISTENERS_DISPATCHING ] = 1;\r
};\r
\r
// todo:\r
// type も保存\r
- listeners[ X_Listeners_.UNLISTENS ] = listeners[ X_Listeners_.UNLISTENS ] || {};\r
- unlistens = listeners[ X_Listeners_.UNLISTENS ][ type ];\r
+ listeners[ X_LISTENERS_UNLISTENS ] = listeners[ X_LISTENERS_UNLISTENS ] || {};\r
+ unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
\r
for( i = 0; i < list.length; ++i ){\r
f = list[ i ];\r
if( !unlistens ){\r
- unlistens = listeners[ X_Listeners_.UNLISTENS ][ type ];\r
+ unlistens = listeners[ X_LISTENERS_UNLISTENS ][ type ];\r
};\r
if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
\r
if( f.once || r & X_Callback_UN_LISTEN ){\r
// dispatch 中に unlisten が作られることがある\r
if( !unlistens ){\r
- unlistens = listeners[ X_Listeners_.UNLISTENS ] || ( listeners[ X_Listeners_.UNLISTENS ] = {} );\r
+ unlistens = listeners[ X_LISTENERS_UNLISTENS ] || ( listeners[ X_LISTENERS_UNLISTENS ] = {} );\r
unlistens = unlistens[ type ] || ( unlistens[ type ] = [] );\r
};\r
unlistens.indexOf( f ) === -1 && ( unlistens[ unlistens.length ] = f );\r
};\r
ret |= X_Type_isFinite( r ) ? r : 0;\r
\r
- if( r & X_Callback_STOP_NOW ){\r
+ if( r & X_Callback_STOP_NOW === X_Callback_STOP_NOW ){\r
sysOnly = true;\r
break;\r
}; \r
};\r
\r
- if( ( --listeners[ X_Listeners_.DISPATCHING ] ) === 0 ){\r
+ if( ( --listeners[ X_LISTENERS_DISPATCHING ] ) === 0 ){\r
\r
- delete listeners[ X_Listeners_.DISPATCHING ];\r
+ delete listeners[ X_LISTENERS_DISPATCHING ];\r
\r
// dispatch 中に listen されたイベントの追加\r
- if( list = listeners[ X_Listeners_.RESERVES ] ){\r
+ if( list = listeners[ X_LISTENERS_RESERVES ] ){\r
for( i = 0, l = list.length; i < l; ++i ){\r
f = list[ i ];\r
X_EventDispatcher_once = f[ 4 ];\r
};\r
list.length = 0;\r
X_EventDispatcher_once = X_EventDispatcher_lock = false;\r
- delete listeners[ X_Listeners_.RESERVES ];\r
+ delete listeners[ X_LISTENERS_RESERVES ];\r
}; \r
\r
// dispatch 中に unlisten されたイベントの削除\r
- if( unlistens = listeners[ X_Listeners_.UNLISTENS ] ){\r
- delete listeners[ X_Listeners_.UNLISTENS ];\r
+ if( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ){\r
+ delete listeners[ X_LISTENERS_UNLISTENS ];\r
\r
// _unlistens に入っている callbackHash は、lock をクリアしている\r
X_EventDispatcher_unlock = true;\r
delete X_EventDispatcher_LAZY_TIMERS[ X_Timer_currentUID ];\r
};\r
\r
- if( listeners[ X_Listeners_.KILL_RESERVED ] ){\r
+ if( listeners[ X_LISTENERS_KILL_RESERVED ] ){\r
/*\r
for( timerID in X_EventDispatcher_LAZY_TIMERS ){\r
if( X_EventDispatcher_LAZY_TIMERS[ timerID ] === this ) return ret;\r
\r
if( !type ) return this;\r
\r
- if( listeners && listeners[ X_Listeners_.DISPATCHING ] ){\r
- if( !listeners[ X_Listeners_.RESERVES ] ) listeners[ X_Listeners_.RESERVES ] = [];\r
- listeners[ X_Listeners_.RESERVES ][ listeners[ X_Listeners_.RESERVES ].length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ];\r
+ if( listeners && listeners[ X_LISTENERS_DISPATCHING ] ){\r
+ if( !listeners[ X_LISTENERS_RESERVES ] ) listeners[ X_LISTENERS_RESERVES ] = [];\r
+ listeners[ X_LISTENERS_RESERVES ][ listeners[ X_LISTENERS_RESERVES ].length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ];\r
return this;\r
};\r
\r
return this;\r
};\r
\r
- if( reserves = listeners[ X_Listeners_.RESERVES ] ){\r
+ if( reserves = listeners[ X_LISTENERS_RESERVES ] ){\r
for( i = reserves.length; i; ){\r
f = reserves[ --i ];\r
if( f[ 0 ] === opt_type && f[ 1 ] === opt_arg1 && f[ 2 ] === opt_arg2 && f[ 3 ] === opt_arg3 && ( !f[ 5 ] || X_EventDispatcher_unlock ) ){\r
reserves.splice( i, 1 );\r
- if( !reserves.legth ) delete listeners[ X_Listeners_.RESERVES ];\r
+ if( !reserves.legth ) delete listeners[ X_LISTENERS_RESERVES ];\r
return this;\r
};\r
};\r
\r
f = ( list = listeners[ opt_type ] )[ i ];\r
\r
- if( unlistens = listeners[ X_Listeners_.UNLISTENS ] ){\r
+ if( unlistens = listeners[ X_LISTENERS_UNLISTENS ] ){\r
// _unlistens に入っている callbackHash は、lock のチェックは済んでいる\r
( unlistens = unlistens[ opt_type ] ) ?\r
( unlistens[ unlistens.length ] = f ) :\r
- ( listeners[ X_Listeners_.UNLISTENS ][ opt_type ] = [ f ] );\r
+ ( listeners[ X_LISTENERS_UNLISTENS ][ opt_type ] = [ f ] );\r
} else {\r
delete f.once;\r
list.splice( i, 1 );\r
// TODO カウンター\r
empty = true;\r
for( k in listeners ){\r
- if( k <= X_Listeners_.KILL_RESERVED ) continue;\r
+ if( k <= X_LISTENERS_KILL_RESERVED ) continue;\r
empty = false;\r
break;\r
};\r
\r
for( type in listeners ){\r
//if( X_EMPTY_OBJECT[ opt_type ] ) continue;\r
- if( type <= X_Listeners_.KILL_RESERVED ) continue;\r
+ if( type <= X_LISTENERS_KILL_RESERVED ) continue;\r
list = listeners[ type ];\r
for( i = list.length; i; ){\r
that[ 'unlisten' ]( type, list[ --i ] );\r
};\r
\r
function X_EventDispatcher_addEvent( that, type, raw, list ){\r
- var i;\r
+ var i, f;\r
X_EventDispatcher_lock || ( type = X_Event_Rename[ type ] || type );\r
\r
if( X_Type_isArray( type ) ){\r
console.log( 'events fix > ' + type[ i ] );\r
};\r
} else {\r
- X_EventDispatcher_actualAddEvent( that, type, raw, list );\r
- };\r
-};\r
-\r
-var X_EventDispatcher_actualAddEvent =\r
+ \r
// Days on the Moon DOM Events とブラウザの実装 \r
// http://nanto.asablo.jp/blog/2007/03/23/1339502\r
// Safari 2 では関数オブジェクトしか EventListener として使えませんが、Safari のナイトリービルドでは handleEvent メソッドを持つオブジェクトも EventListener として使えるようです。\r
- X_UA_EVENT.W3C ?\r
- (function( that, type, raw, list ){\r
- var f;\r
+ \r
+ if( X_UA_EVENT.W3C ){\r
switch( that[ '_rawType' ] ){\r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
- list.sltoken = raw.AddEventListener( type, list.slcallback );\r
+ list.sltoken = raw[ 'AddEventListener' ]( type, list.slcallback );\r
break;\r
\r
case X_EventDispatcher_EVENT_TARGET_XHR :\r
type === 'animationiteration' || type === 'webkitAnimationIteration' ) ){\r
raw.addEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false );\r
} else {\r
- f = that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+ f = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
\r
if( raw.addEventListener ){\r
raw.addEventListener( type, f, false );\r
};\r
};\r
};\r
- }) :\r
- X_UA_EVENT.IE ?\r
- (function( that, type, raw, list ){\r
- var f;\r
+ } else\r
+ if( X_UA_EVENT.IE ){\r
switch( that[ '_rawType' ] ){ \r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
- list.sltoken = raw.AddEventListener( type, list.slcallback );\r
+ list.sltoken = raw[ 'AddEventListener' ]( type, list.slcallback );\r
break; \r
\r
case X_EventDispatcher_EVENT_TARGET_XHR :\r
break;\r
\r
default :\r
- f = that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+ f = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
\r
if( raw.attachEvent ){\r
raw.attachEvent( 'on' + type, f );\r
};\r
break;\r
};\r
- }) :\r
- (function( that, type, raw, list ){\r
+ } else {\r
switch( that[ '_rawType' ] ){\r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
// DOM0 で Silverlight ってあるの -> ie4 mobile?\r
list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
- list.sltoken = raw.AddEventListener( type, list.slcallback );\r
+ list.sltoken = raw[ 'AddEventListener' ]( type, list.slcallback );\r
break; \r
\r
case X_EventDispatcher_EVENT_TARGET_XHR :\r
break;\r
\r
default :\r
- raw[ 'on' + type ] = that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
+ raw[ 'on' + type ] = that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] || ( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
break;\r
};\r
- });\r
+ }\r
+ };\r
+};\r
+\r
\r
/*\r
* iOS の webkitTransitionEnd が連続して起こる場合、\r
- * コールバックの(that[ X_Listeners_.ACTUAL_HANDLER ])クロージャ内の実際のコールバック(X_Callback_actualClosure:obj._)が\r
+ * コールバックの(that[ X_LISTENERS_ACTUAL_HANDLER ])クロージャ内の実際のコールバック(X_Callback_actualClosure:obj._)が\r
* 参照できていない問題に遭遇、、、iOS3.1.3 & iOS6.1.5 で確認\r
* animation も怪しい、、、\r
*/\r
X_EventDispatcher_systemUnlisten( that, type[ --i ], X_emptyFunction );\r
};\r
} else {\r
- X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip );\r
- };\r
-};\r
-\r
-var X_EventDispatcher_actualRemoveEvent =\r
- X_UA_EVENT.W3C ?\r
- (function( that, type, raw, list, skip ){\r
+ if( X_UA_EVENT.W3C ){\r
switch( that[ '_rawType' ] ){\r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
- raw.RemoveEventListener( type, list.sltoken ); // token\r
+ raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
X_Callback_correct( list.slcallback );\r
delete list.sltoken;\r
delete list.slcallback;\r
raw.removeEventListener( type, X_EventDispatcher_iOSTransitionEndDispatch, false );\r
} else \r
if( raw.addEventListener ){\r
- raw.removeEventListener( type, that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ], false );\r
+ raw.removeEventListener( type, that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ], false );\r
} else {\r
raw[ 'on' + type ] = null;\r
};\r
\r
- if( !skip && that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] ){\r
- X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
- delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+ if( !skip && that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] ){\r
+ X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+ delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
};\r
};\r
- }) :\r
- X_UA_EVENT.IE ?\r
- (function( that, type, raw, list, skip ){\r
+ } else\r
+ if( X_UA_EVENT.IE ){\r
switch( that[ '_rawType' ] ){\r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
- raw.RemoveEventListener( type, list.sltoken ); // token\r
+ raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
X_Callback_correct( list.slcallback );\r
delete list.sltoken;\r
delete list.slcallback;\r
\r
default :\r
if( raw.attachEvent ){\r
- raw.detachEvent( 'on' + type, that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
+ raw.detachEvent( 'on' + type, that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
} else {\r
raw[ 'on' + type ] = X_emptyFunction;\r
raw[ 'on' + type ] = '';\r
};\r
\r
if( !skip ){\r
- X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
- delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+ X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+ delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
};\r
};\r
- }) :\r
- (function( that, type, raw, list, skip ){\r
+ } else {\r
switch( that[ '_rawType' ] ){\r
case X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT :\r
- raw.RemoveEventListener( type, list.sltoken ); // token\r
+ raw[ 'RemoveEventListener' ]( type, list.sltoken ); // token\r
X_Callback_correct( list.slcallback );\r
delete list.sltoken;\r
delete list.slcallback;\r
raw[ 'on' + type ] = '';\r
\r
if( !skip ){\r
- X_Callback_correct( that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ] );\r
- delete that[ '_listeners' ][ X_Listeners_.ACTUAL_HANDLER ];\r
+ X_Callback_correct( that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ] );\r
+ delete that[ '_listeners' ][ X_LISTENERS_ACTUAL_HANDLER ];\r
};\r
};\r
- });\r
+ };\r
+ };\r
+};\r
\r
\r
// TODO ブラウザからの呼び出しの最後に登録された関数を呼び出す機能(例えば画面の更新)\r
if( !list || !raw ) return;\r
for( type in list ){\r
//if( X_EMPTY_OBJECT[ type ] ) continue;\r
- //if( type <= X_Listeners_.KILL_RESERVED ) continue;\r
+ //if( type <= X_LISTENERS_KILL_RESERVED ) continue;\r
// 数字イベントの除外\r
if( !X_String_isNumberString( type ) ){\r
// TODO type rename はここ\r
return X_ViewPort[ 'dispatch' ]( X_EVENT_BEFORE_UNLOAD );
case 'unload' :
+ //https://developer.mozilla.org/ja/docs/Web/JavaScript/A_re-introduction_to_JavaScript
+ //Firefox 1.5 の bfcache が無効になりますので、他に理由がない限り Firefox では unload リスナを登録するべきではないことに注意してください。
X_ViewPort[ 'dispatch' ]( X_EVENT_UNLOAD );
//alert('unload');
X_ViewPort_document[ 'kill' ]();
X_ViewPort_rootElement = document.compatMode !== 'CSS1Compat' ? elmBody : elmHtml || elmBody;
- html = X[ 'Doc' ][ 'html' ] = X_Node_html = elmHtml && new Node( elmHtml )[ 'removeClass' ]( 'js-disabled' )[ 'addClass' ]( X_UA_classNameForHTML );
+ /**
+ * Node( documentElement )
+ * @alias X.Doc.html
+ * @type {Node}
+ */
+ X[ 'Doc' ][ 'html' ] = html = X_Node_html = elmHtml && new Node( elmHtml )[ 'removeClass' ]( 'js-disabled' )[ 'addClass' ]( X_UA_classNameForHTML );
html[ '_flags' ] |= X_Node_State.IN_TREE;
-
- head = X[ 'Doc' ][ 'head' ] = X_Node_head = elmHead && new Node( elmHead );
-
- body = X[ 'Doc' ][ 'body' ] = X_Node_body = new Node( elmBody );
+
+ /**
+ * Node( head )
+ * @alias X.Doc.head
+ * @type {Node}
+ */
+ X[ 'Doc' ][ 'head' ] = head = X_Node_head = elmHead && new Node( elmHead );
+
+ /**
+ * Node( documentElement )
+ * @alias X.Doc.body
+ * @type {Node}
+ */
+ X[ 'Doc' ][ 'body' ] = body = X_Node_body = new Node( elmBody );
body[ 'parent ' ] = head[ 'parent' ] = html;
html[ '_xnodes' ] = [ head, body ];
* @alias X.Doc
*/
X[ 'Doc' ] = {
+ /**
+ * EventDispatcher.prototype.listen 参照
+ * @alias X.Doc.listen
+ */
'listen' : function( type, arg1, arg2, arg3 ){
if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){
/*
return X[ 'Doc' ];
},
-
+ /**
+ * EventDispatcher.prototype.listenOnce 参照
+ * @alias X.Doc.listenOnce
+ */
'listenOnce' : function( type, arg1, arg2, arg3 ){
if( type <= X_ViewPort_readyState && type === 'DOMContentLoaded' ){
/*
type && arg1 && X_ViewPort_document[ 'listenOnce' ]( type, arg1, arg2, arg3 );
return X[ 'Doc' ];
},
-
+
+ /**
+ * EventDispatcher.prototype.unlisten 参照
+ * @alias X.Doc.unlisten
+ */
'unlisten' : function( type, arg1, arg2, arg3 ){
type && arg1 && X_ViewPort_document[ 'unlisten' ]( type, arg1, arg2, arg3 );
return X[ 'Doc' ];
},
-
+
+ /**
+ * EventDispatcher.prototype.listening 参照
+ * @alias X.Doc.listening
+ */
'listening' : function( type, arg1, arg2, arg3 ){
return X_ViewPort_document[ 'listening' ]( type, arg1, arg2, arg3 );
},
'create' : X_Doc_create,
'createText' : X_Doc_createText
-
- // html
- // head
- // body
- // find
+
};
/**
})( X_Node_Attr_renameForDOM, X_Node_Attr_renameForTag );\r
\r
\r
-\r
-/* --------------------------------------\r
- * attribute\r
- * \r
- * http://nanto.asablo.jp/blog/2005/10/29/123294\r
- * className, onclick等 はここで設定しない\r
- * \r
+/**\r
+ * 属性の getter と setter。onclick等はできないので listen, listenOnce を使うこと。http://nanto.asablo.jp/blog/2005/10/29/123294\r
+ * @alias Node.prototype.attr\r
+ * @param {string|object} [nameOrObj] 属性名、または追加する属性のハッシュ\r
+ * @param {string|number} [value=] 属性の値\r
+ * @return {Node|string|number} getter の場合は値を、setter の場合は自身を返す。(メソッドチェーン)\r
+ * @example // getter\r
+ * node.attr( 'tagName' ) === 'DIV';\r
+ * // setter - 1\r
+ * node.attr( { src : url, width : 100, height : 100 } );\r
+ * // setter - 2\r
+ * node.attr( 'src', url );\r
*/\r
Node.prototype[ 'attr' ] = function( nameOrObj /* v */ ){\r
var attrs = this[ '_attrs' ], newAttrs, f, k, elm, v;\r
// unitID, name 単位指定のプロパティ取得 geter
// obj setter
// name, value setter
-
-Node.prototype[ 'css' ] = function( nameOrObj /* orUnitID, valuOrUnitOrName */ ){
+/**
+ * style の getter と setter。
+ * @alias Node.prototype.css
+ * @param {string|object} [nameOrObj] style 名、または追加する style のハッシュ
+ * @param {string|number} [value=] style の値
+ * @return {Node|string|number} getter の場合は値を、setter の場合は自身を返す。(メソッドチェーン)
+ * @example // getter
+ * node.css( 'color' );
+ * // setter - 1
+ * node.css( { width : w + 'px', height : h + 'px' } );
+ * // setter - 2
+ * node.css( 'color', 0x666666 );
+ */
+Node.prototype[ 'css' ] = function( nameOrObj /* value */ ){
var args = arguments,
css = this[ '_css' ],
p, name, v, plain, camelize, flags;
};
// getter
if( !css ) return;
- // 集計 border, padding, margin, backgroundPosition, clip
+ // TODO 集計 border, padding, margin, backgroundPosition, clip
// TODO border で正確なデータを返せない時は、null を返す
return css[ X_Node_CSS_camelize( nameOrObj ) ];
};
// TODO { a : 1, A : 2, _ : 3,,, }\r
X_Node_Selector__ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789\\',\r
X_Node_Selector__NUMBER = '+-0123456789';\r
-\r
-// XMLWrapper のために今だけ外部に公開\r
-X_NodeSelector_parse = X_Node_Selector__parse;\r
\r
/*\r
* セレクタ文字列の解析、但し一挙に行わず、ひと塊づつ\r
return not ? [ i + tmp + 1, [ combinator, 6, result ] ] : [ i, result ];\r
};\r
\r
- // セレクター\r
+ /**\r
+ * selector を使って Node を取得する\r
+ * @alias X.Doc.find\r
+ * @function\r
+ * @param {string} セレクター文字列\r
+ * @return {Node|NodeList}\r
+ */\r
X[ 'Doc' ][ 'find' ] = X_shortcutFunction = Node.prototype[ 'find' ] = X_NodeList.prototype[ 'find' ] = function ( queryString ){\r
var HTML = X_Node_html,\r
scope = this.constructor === X_NodeList && this.length ? this : [ this.constructor === Node ? this : X_Node_body ],\r
\r
var\r
ease = {\r
- quadratic: {\r
+ 'quadratic' : {\r
style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',\r
fn: function (k) {\r
return k * ( 2 - k );\r
}\r
},\r
- circular: {\r
+ 'circular' : {\r
style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)\r
fn: function (k) {\r
return Math.sqrt( 1 - ( --k * k ) );\r
}\r
},\r
- back: {\r
+ 'back' : {\r
style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',\r
fn: function (k) {\r
var b = 4;\r
return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;\r
}\r
},\r
- bounce: {\r
+ 'bounce' : {\r
style: '',\r
fn: function (k) {\r
if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {\r
}\r
}\r
},\r
- elastic: {\r
+ 'elastic' : {\r
style: '',\r
fn: function (k) {\r
var f = 0.22,\r
// 中断\r
\r
/*\r
+ * TODO : DX Anime\r
* TODO : scale, ActiveX transform, zoom, fontSizeScale\r
* TODO : rotate, ActiveX transform -> 位置補正のために size 情報が必要なので、commitUpdate 後に計算して適用\r
* TODO : matrix\r
obj = this[ '_anime' ] || ( this[ '_anime' ] = {} );\r
\r
obj.duration = 0 <= duration && X_Type_isFinite( duration ) ? duration : 500;\r
- obj.easing = ease[ easing ] || ease.circular;\r
+ obj.easing = ease[ easing ] || ease[ 'circular' ];\r
// 現在 GPUレイヤーのtop になっているか?将来については phase で判定\r
obj.gpuParent = obj.gpuParent || false;\r
obj.phase = duration === 0 ? 9 : obj.phase === 9 ? 9 : 0; //\r
obj.wait = X_Type_isFinite( wait ) ? wait : 1000;\r
\r
obj.startTime = X_Timer_now();\r
- obj.startX = ( start.x || start.x === 0 ) ? start.x : obj.x || 0;\r
- obj.startY = ( start.y || start.y === 0 ) ? start.y : obj.y || 0;\r
+ obj.startX = ( start.x || start.x === 0 ) ? start.x : obj.x || NaN;\r
+ obj.startY = ( start.y || start.y === 0 ) ? start.y : obj.y || NaN;\r
obj.startA = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : obj.a || 1;\r
\r
obj.destTime = obj.startTime + obj.duration;\r
- obj.destX = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || 0;\r
- obj.destY = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || 0;\r
+ obj.destX = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || NaN;\r
+ obj.destY = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || NaN;\r
obj.destA = 0 <= dest.opacity && dest.opacity <= 1 ? dest.opacity : obj.destA || 1;\r
\r
X_Node_ANIMATIONS.indexOf( this ) === -1 &&\r
if( X_Node_Anime_hasTransform ){\r
xnode[ 'css' ]({\r
transform : 'translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)' + ( useGPU ? X_Node_Anime_translateZ : '' ),\r
- opacity : opacity\r
+ opacity : opacity === 1 ? '' : opacity\r
});\r
} else {\r
- xnode[ 'css' ]({\r
+ x === x && xnode[ 'css' ]({\r
left : ( x | 0 ) + 'px',\r
+ opacity : opacity === 1 ? '' : opacity });\r
+ y === y && xnode[ 'css' ]({\r
top : ( y | 0 ) + 'px',\r
- opacity : opacity\r
- });\r
+ opacity : opacity === 1 ? '' : opacity });\r
};\r
\r
if( X_Node_Anime_translateZ ){\r
parseFloat( navigator.plugins[ 'Shockwave Flash' ].version ) :
!X_UA[ 'IE4' ] && !X_UA[ 'IE5' ] && X_UA[ 'ActiveX' ] ? (function(){
var obj = eval( 'var a,e;try{a=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}a' ),
- ver = obj && obj.GetVariable( '$version' ).split( ' ' ).join( '.' );
+ ver = obj && obj[ 'GetVariable' ]( '$version' ).split( ' ' ).join( '.' );
return parseFloat( ver ) || 0;
})() :
0,
X_Pulgin_SILVER_LIGHT_VERSION =
!X_UA[ 'IE' ] && navigator.plugins[ 'Silverlight Plug-In' ] ?
parseFloat( navigator.plugins[ 'Silverlight Plug-In' ].version ) :
- X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] /* && !X_UA[ 'IECompat' ] */ ? (function(){
+ X_UA[ 'ActiveX' ] && 6 <= X_UA[ 'IE' ] ? (function(){
return eval( 'var a,i=0;try{a=new ActiveXObject("AgControl.AgControl");for(i=5;i;--i)if(a.IsVersionSupported(i+".0"))break;}catch(e){i=0}i' );
})() :
0,
--- /dev/null
+/*\r
+ * XMLWrapper_find 周りの オリジナルコードに関する情報\r
+ * Original code by pettanR team\r
+ * - http://sourceforge.jp/projects/pettanr/scm/git/clientJs/blobs/master/0.6.x/js/01_dom/18_XDomQuery.js\r
+ * and\r
+ * Original code by ofk ( kQuery, ksk )\r
+ * - http://d.hatena.ne.jp/ofk/comment/20090106/1231258010\r
+ * - http://d.hatena.ne.jp/ofk/20090111/1231668170\r
+ */\r
+\r
+X[ 'XML' ] = XMLWrapper;\r
+\r
+function XMLWrapper( xml ){\r
+ this._rawXML = xml;\r
+};\r
+\r
+XMLWrapper.prototype.length = 1;\r
+XMLWrapper.prototype.has = XMLWrapper_has;\r
+XMLWrapper.prototype.get = XMLWrapper_get;\r
+XMLWrapper.prototype.val = XMLWrapper_val;\r
+XMLWrapper.prototype.find = XMLWrapper_find;\r
+\r
+function XMLWrapper_has( queryString ){\r
+ return !!this.find( queryString ).length;\r
+};\r
+\r
+function XMLWrapper_get( index ){\r
+ if( this.length === 1 ) return this;\r
+ if( this.length === 0 ) return null;\r
+ // 一度発行した XMLWrapper は控えて置いて再発行する。\r
+ if( this._wraps && this._wraps[ index ] ) return this._wraps[ index ];\r
+ if( !this._wraps ) this._wraps = [];\r
+ return this[ index ] ?\r
+ ( this._wraps[ index ] = new XMLWrapper( this[ index ] ) ) :\r
+ null;\r
+};\r
+\r
+function XMLWrapper_val( queryString, type ){\r
+ var attr_textContent = X.UA.IE < 9 || X.UA.Opera ? 'innerText' : X.UA.IE9 ? 'text' : 'textContent',\r
+ wrapper, xml, v;\r
+ \r
+ switch( queryString ){\r
+ case 'number' :\r
+ case 'int' :\r
+ case 'boolean' :\r
+ case 'string' :\r
+ case undefined :\r
+ type = queryString;\r
+ queryString = undefined;\r
+ };\r
+ \r
+ wrapper = queryString ? this.find( queryString ) : this;\r
+ xml = wrapper.length === 1 ? wrapper._rawXML : wrapper[ 0 ];\r
+\r
+ if( !xml ){\r
+ switch( type ){\r
+ case 'number' :\r
+ case 'int' :\r
+ return NaN;\r
+ case 'boolean' :\r
+ return false;\r
+ case 'string' :\r
+ return '';\r
+ default :\r
+ return null;\r
+ };\r
+ };\r
+ \r
+ v = xml.nodeType === 1 ? xml.innerText || xml.text || xml.textContent : xml.nodeValue;\r
+ //xml.toStrign()\r
+ switch( type ){\r
+ case 'number' :\r
+ return parseFloat( v );\r
+ case 'int' :\r
+ return parseInt( v );\r
+ case 'boolean' :\r
+ return v && v !== '0' && v !== 'false' && v !== 'null' && v !== 'undefined' && v !== 'NaN';\r
+ //case 'string' :\r
+ //default : \r
+ };\r
+ return v || '';\r
+};\r
+\r
+ function XMLWrapper_find( queryString ){\r
+\r
+ var scope = this.constructor === XMLListWrapper ? this : [ this._rawXML ],\r
+ parents = scope, // 探索元の親要素 xmlList の場合あり\r
+ ARY_PUSH = Array.prototype.push,\r
+ ret = [], // 結果要素\r
+ isXML = true,\r
+ isMulti = 1 < scope.length,// 要素をマージする必要がある\r
+ isStart = true,\r
+ _ = ' ',\r
+ isAll, isNot, hasRoot,\r
+ l, i, n, parsed,\r
+ xmlList, // 一時保存用\r
+ merge, // 要素がコメントノードで汚染されている場合使う\r
+ combinator, selector, name, tagName,\r
+ uid, tmp, xml, filter, key, op, val, toLower, useName,\r
+ links, className, attr, flag;\r
+\r
+ // 文字列以外は空で返す\r
+ if( !X_Type_isString( queryString ) ) return XMLListWrapper_0;\r
+ \r
+ xmlList = [];\r
+ \r
+ // 以下、パースと探索\r
+ for( ; queryString.length; ){\r
+ //console.log( 'queryString[' + queryString + ']' );\r
+ \r
+ // 初期化処理\r
+ if( !parsed ){\r
+ parsed = X_Node_Selector__parse( queryString );\r
+ \r
+ if( X_Type_isNumber( parsed ) ){\r
+ // error\r
+ return XMLListWrapper_0;\r
+ };\r
+ \r
+ queryString = queryString.substr( parsed[ 0 ] );\r
+ parsed = parsed[ 1 ];\r
+ \r
+ if( parsed === 5 ){\r
+ isMulti = true;\r
+ parents = scope;\r
+ xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );\r
+ parsed = null;\r
+ xmlList = [];\r
+ isStart = true;\r
+ continue;\r
+ };\r
+ };\r
+ \r
+ combinator = parsed[ 0 ];\r
+ selector = parsed[ 1 ];\r
+ name = parsed[ 2 ];\r
+ tagName = selector === 1 ? name : '*';\r
+ isAll = tagName === '*';\r
+ \r
+ if( !isStart ){\r
+ if( !xmlList.length ){\r
+ parsed = null;\r
+ continue; \r
+ } else\r
+ if( combinator !== 0 ){\r
+ parents = xmlList;\r
+ xmlList = [];\r
+ //console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xmlList.length );\r
+ };\r
+ };\r
+ \r
+ i = 0;\r
+ l = parents.length;\r
+ n = -1; \r
+ isMulti = isMulti || 1 < l;\r
+ \r
+ console.log( 'combinator ' + combinator );\r
+ \r
+ switch( combinator ){\r
+ // > TagName|*\r
+ case 2 :\r
+ for( ; i < l; ++i ){\r
+ for( xml = parents[ i ].firstChild; xml; xml = xml.nextSibling ){\r
+ if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ) xmlList[ ++n ] = xml;\r
+ }; \r
+ };\r
+ break;\r
+ // + TagName|*\r
+ case 3 :\r
+ for( ; i < l; ++i ){\r
+ for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){\r
+ if( xml.nodeType === 1 ){\r
+ if( isAll || tagName === xml.tagName ) xmlList[ ++n ] = xml;\r
+ break; \r
+ };\r
+ }; \r
+ };\r
+ break;\r
+ // ~ TagName|*\r
+ case 4 :\r
+ merge = [];\r
+ for( ; i < l; ++i ){\r
+ for( xml = parents[ i ].nextSibling; xml; xml = xml.nextSibling ){\r
+ if( xml.nodeType === 1 && ( isAll || tagName === xml.tagName ) ){\r
+ if( merge.indexOf( xml ) !== -1 ){\r
+ break;\r
+ } else {\r
+ merge[ merge.length ] = xml;\r
+ xmlList[ ++n ] = xml;\r
+ };\r
+ }; \r
+ }; \r
+ };\r
+ break;\r
+\r
+ // @ 属性ノード\r
+ case 6 :\r
+ selector = 0;\r
+ tagName = '*';\r
+ for( ; i < l; ++i ){\r
+ if( xml = parents[ i ].getAttributeNode( name ) ){\r
+ xmlList[ ++n ] = xml;\r
+ };\r
+ };\r
+ break;\r
+ default :\r
+ if( combinator === 1 || ( isStart && selector < 7 ) ){\r
+ console.log( l + ' > ' + xmlList.length + ' tag:' + tagName );\r
+ for( ; i < l; ++i ){\r
+ xml = parents[ i ];\r
+ xml.childNodes && xml.childNodes.length && XMLWrapper_fetchElements( xmlList, xml, isAll ? null : tagName );\r
+ };\r
+ console.log( l + ' >> ' + xmlList.length + ' tag:' + tagName );\r
+ };\r
+ };\r
+ \r
+ isStart = false;\r
+ \r
+ //alert( 'pre-selector:' + ( xmlList && xmlList.length ) )\r
+ \r
+ switch( selector ){\r
+ // #, ID\r
+ case 2 :\r
+ filter = [ 'id', 1, name ]; break;\r
+ // ., class\r
+ case 3 :\r
+ filter = [ 'class', 3 /*'~='*/, name ]; break;\r
+ // :, 擬似クラス\r
+ case 4 :\r
+ if( !( filter = XMLWrapper_filter[ name ] ) ){\r
+ return XMLListWrapper_0;;\r
+ };\r
+ break;\r
+ // [] 属性\r
+ case 5 :\r
+ filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
+ // :not\r
+ case 6 :\r
+ isNot = true;\r
+ parsed = parsed[ 2 ];\r
+ name = parsed[ 2 ];\r
+ switch( parsed[ 1 ] ) {\r
+ case 1 :\r
+ filter = [ 'tag', 1, name ]; break;\r
+ // #, ID\r
+ case 2 :\r
+ filter = [ 'id', 1, name ]; break;\r
+ // ., class\r
+ case 3 :\r
+ filter = [ 'class', 3, name ]; break;\r
+ // :, 擬似クラス\r
+ case 4 :\r
+ if( !( filter = X_Node_Selector__filter[ name ] ) ){\r
+ return [];\r
+ };\r
+ break;\r
+ // [] 属性\r
+ case 5 :\r
+ filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
+ };\r
+ break;\r
+ // scope\r
+ case 7 :\r
+ xmlList = scope; break;\r
+ /* root\r
+ case 8 :\r
+ hasRoot = true;\r
+ xmlList = [ HTML ]; break;\r
+ // link\r
+ case 9 :\r
+ if( links = document.links ){\r
+ for( xmlList = [], i = links.length; i; ){\r
+ xmlList[ --i ] = new Node( links[ i ] );\r
+ };\r
+ } else {\r
+ // area[href],a[href]\r
+ }; */\r
+ };\r
+ \r
+ if( filter && xmlList.length ){\r
+ // filter.mが関数の場合\r
+ if( filter.m ){\r
+ xmlList = filter.m(\r
+ {\r
+ not : isNot,\r
+ xml : isXML\r
+ },\r
+ xmlList,\r
+ parsed[ 3 ], parsed[ 4 ]\r
+ );\r
+ } else\r
+ // filterが関数の場合\r
+ if( X_Type_isFunction( filter ) ){\r
+ tmp = [];\r
+ for( i = 0, n = -1; xml = xmlList[ i ]; ++i ){\r
+ if( ( !!filter( xml ) ) ^ isNot ) tmp[ ++n ] = xml; \r
+ };\r
+ xmlList = tmp;\r
+ } else {\r
+ // 属性セレクター \r
+ tmp = [];\r
+ key = filter[ 0 ];\r
+ op = filter[ 1 ];\r
+ val = filter[ 2 ];\r
+\r
+ // 通常\r
+ if( op === 3 ) val = _ + val + _;\r
+\r
+ for( i = 0, n = -1, l = xmlList.length; i < l; ++i ){\r
+ xml = xmlList[ i ];\r
+ attr = elem.getAttribute( key, 2 );\r
+ flag = attr != null;// && ( !useName || attr !== '' );\r
+ if( flag && op ){\r
+ //if( toLower ) attr = attr.toLowerCase();\r
+ \r
+ switch( op ){\r
+ case 1: // =\r
+ flag = attr === val;\r
+ break;\r
+ case 2: // !=\r
+ flag = attr !== val;\r
+ break;\r
+ case 3: // ~=\r
+ flag = ( _ + attr + _ ).indexOf( val ) !== -1;\r
+ break;\r
+ case 4: // ^=\r
+ flag = attr.indexOf( val ) === 0;\r
+ break;\r
+ case 5: // $=\r
+ flag = attr.lastIndexOf( val ) + val.length === attr.length;\r
+ break;\r
+ case 6: // *=\r
+ flag = attr.indexOf( val ) !== -1;\r
+ break;\r
+ case 7: // |=\r
+ flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';\r
+ break;\r
+ };\r
+ };\r
+ if( !!flag ^ isNot ) tmp[ ++n ] = xml;\r
+ //};\r
+ };\r
+ xmlList = tmp;\r
+ };\r
+ };\r
+ filter = null;\r
+ isNot = false;\r
+ parsed = null;\r
+ \r
+ //console.log( '//end :' + ( xmlList && xmlList.length ) );\r
+ };\r
+ //console.log( 'multi:' + ( xmlList && xmlList.length ) );\r
+ \r
+ // tree 順に並び替え、同一要素の排除\r
+ if( isMulti ){\r
+ xmlList && xmlList.length && ARY_PUSH.apply( ret, xmlList );\r
+ l = ret.length;\r
+ if( l === 0 ) return XMLListWrapper_0;\r
+ if( l === 1 ) return new XMLWrapper( ret[ 0 ] );\r
+ \r
+ xmlList = [];\r
+ //merge = [];\r
+ for( i = 0, n = -1; i < l; ++i ){\r
+ //alert( 'multi:' + i )\r
+ xml = ret[ i ];\r
+ if( xmlList.indexOf( xml ) === -1 ){\r
+ //merge[ merge.length ] = xml;\r
+ xmlList[ ++n ] = xml;\r
+ };\r
+ };\r
+ XMLWrapper_sortElementOrder( ret = [], xmlList, this._rawXML.childNodes );\r
+ \r
+ // @\r
+ for( i = 0, l = xmlList.length; i < l; ++i ){\r
+ if( ret.indexOf( xml = xmlList[ i ] ) === -1 ){\r
+ ret[ ret.length ] = xml;\r
+ };\r
+ };\r
+ \r
+ xmlList = ret;\r
+ };\r
+\r
+ return xmlList.length === 1 ? new XMLWrapper( xmlList[ 0 ] ) : new XMLListWrapper( xmlList );\r
+ };\r
+\r
+ function XMLWrapper_sortElementOrder( newList, list, xmlList ){\r
+ var l = xmlList.length,\r
+ i = 0,\r
+ j, child, _xmlList;\r
+ for( ; i < l; ++i ){\r
+ child = xmlList[ i ];\r
+ //if( child.nodeType !== 1 ) continue;\r
+ //console.log( child.tagName );\r
+ if( ( j = list.indexOf( child ) ) !== -1 ){\r
+ newList[ newList.length ] = child;\r
+ list.splice( j, 1 );\r
+ if( list.length === 1 ){\r
+ newList[ newList.length ] = list[ 0 ];\r
+ list.length = 0;\r
+ return true;\r
+ };\r
+ if( list.length === 0 ) return true;\r
+ };\r
+ if( ( _xmlList = child.childNodes ) && XMLWrapper_sortElementOrder( newList, list, _xmlList ) ){\r
+ return true;\r
+ };\r
+ };\r
+ };\r
+ \r
+ function XMLWrapper_fetchElements( list, parent, tag ){\r
+ var xmlList = parent.childNodes,\r
+ l = xmlList.length,\r
+ i = 0,\r
+ child;\r
+ for( ; i < l; ++i ){\r
+ child = xmlList[ i ];\r
+ if( child.nodeType === 1 ){\r
+ ( !tag || child.tagName === tag ) && ( list[ list.length ] = child );\r
+ //console.log( parent.tagName + ' > ' + child.tagName + ' == ' + tag+ ' l:' + list.length );\r
+ child.childNodes && child.childNodes.length && XMLWrapper_fetchElements( list, child, tag );\r
+ };\r
+ };\r
+ };\r
+\r
+ function XMLWrapper_funcSelectorChild( type, flag_all, flags, xmlList ){\r
+ var res = [],\r
+ flag_not = flags.not,\r
+ i = 0, n = -1, xnode, node,\r
+ tagName, tmp;\r
+ for( ; xnode = xmlList[ i ]; ++i ){\r
+ tagName = flag_all || xnode.tagName;\r
+ tmp = null;\r
+ if( /* tmp === null && */ type <= 0 ){\r
+ for( node = xnode.previousSibling; node; node = node.previousSibling ){\r
+ if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+ tmp = false;\r
+ break;\r
+ };\r
+ };\r
+ };\r
+ if( tmp === null && 0 <= type ){\r
+ for( node = xnode.nextSibling; node; node = node.nextSibling ){\r
+ if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+ tmp = false;\r
+ break;\r
+ }; \r
+ }; \r
+ };\r
+ if( tmp === null ) tmp = true;\r
+ if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+ };\r
+ return res;\r
+ };\r
+ function XMLWrapper_funcSelectorNth( pointer, sibling, flag_all, flags, xmlList, a, b ){\r
+ var res = [],\r
+ checked = {},\r
+ flag_not = flags.not,\r
+ i = 0, n = -1, uid,\r
+ c, xnode, tmp, node, tagName;\r
+ for( ; xnode = xmlList[ i ]; ++i ){\r
+ uid = xnode._uid;\r
+ tmp = checked[ uid ];\r
+ if( tmp === void 0 ){\r
+ for( c = 0, node = xnode.parentNode[ pointer ], tagName = flag_all || xnode.tagName; node; node = node[ sibling ] ){\r
+ if( node.nodeType === 1 && ( flag_all || tagName === node.tagName ) ){\r
+ ++c;\r
+ checked[ node._uid ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;\r
+ }; \r
+ };\r
+ tmp = checked[ uid ];\r
+ };\r
+ if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+ };\r
+ return res;\r
+ };\r
+ function XMLWrapper_funcSelectorProp( prop, flag, flags, xmlList ){\r
+ var res = [],\r
+ flag_not = flag ? flags.not : !flags.not,\r
+ i = 0, n = -1, xnode;\r
+ for( ; xnode = xmlList[ i ]; ++i ){\r
+ if( xnode.getAttributeNode( prop ) ^ flag_not ) res[ ++n ] = xnode;\r
+ };\r
+ return res;\r
+ };\r
+\r
+var XMLWrapper_filter = {\r
+ 'first-child' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, true, flags, xmlList ); }\r
+ },\r
+ 'last-child' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, true, flags, xmlList ); }\r
+ },\r
+ 'only-child' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, true, flags, xmlList ); }\r
+ },\r
+ 'first-of-type' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( -1, false, flags, xmlList ); }\r
+ },\r
+ 'last-of-type' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 1, false, flags, xmlList ); }\r
+ },\r
+ 'only-of-type' : {\r
+ m : function( flags, xmlList ){ return XMLWrapper_funcSelectorChild( 0, false, flags, xmlList ); }\r
+ },\r
+ 'nth-child' : {\r
+ m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', true, flags, xmlList, a, b ); }\r
+ },\r
+ 'nth-last-child' : {\r
+ m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', true, flags, xmlList, a, b ); }\r
+ },\r
+ 'nth-of-type' : {\r
+ m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'firstChild', 'nextSibling', false, flags, xmlList, a, b ); }\r
+ },\r
+ 'nth-last-of-type' : {\r
+ m : function( flags, xmlList, a, b ){ return XMLWrapper_funcSelectorNth( 'lastChild', 'previousSibling', false, flags, xmlList, a, b ); }\r
+ },\r
+ empty : {\r
+ m : function( flags, xmlList ){\r
+ var res = [],\r
+ flag_not = flags.not,\r
+ i = 0, n = -1, xnode, tmp, node;\r
+ for( ; xnode = xmlList[i]; ++i ){\r
+ tmp = true;\r
+ for( node = xnode.firstChild; node; node = node.nextSibling ){\r
+ if( node.nodeType === 1 || ( node.nodeType === 3 && node._text ) ){\r
+ tmp = false;\r
+ break;\r
+ }; \r
+ };\r
+ if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
+ };\r
+ return res;\r
+ }\r
+ },\r
+ contains : {\r
+ m : function( flags, xmlList, arg ){\r
+ var res = [],\r
+ flag_not = flags.not,\r
+ i = 0, n = -1, xnode, text = '',\r
+ // kquery\r
+ attr_textContent = X_UA[ 'IE' ] < 9 || X_UA[ 'Opera' ] ? 'innerText' : X_UA[ 'IE9' ] ? 'text' : 'textContent';\r
+ for( ; xnode = xmlList[ i ]; ++i ){\r
+ switch( xnode.nodeType ){\r
+ case 1 :\r
+ text = xml.nodeType === 1 ? xml.innerText || xml.text || xml.textContent : xml.nodeValue;// xnode[ attr_textContent ];\r
+ break;\r
+ case 2 :\r
+ case 3 :\r
+ text = xnode.nodeValue;\r
+ break;\r
+ };\r
+ console.log( text + ' ' + arg );\r
+ if ( ( -1 < text.indexOf( arg ) ) ^ flag_not ) res[ ++n ] = xnode; \r
+ };\r
+ return res;\r
+ }\r
+ }\r
+};\r
+\r
+function XMLListWrapper( xmlList ){\r
+ var i = 0, l = xmlList ? xmlList.length : 0;\r
+ for( ; i < l; ++i ){\r
+ this[ i ] = xmlList[ i ];\r
+ };\r
+ this.length = l;\r
+};\r
+\r
+var XMLListWrapper_0 = new XMLListWrapper();\r
+\r
+XMLListWrapper.prototype.length = 0;\r
+XMLListWrapper.prototype._wraps = null;\r
+XMLListWrapper.prototype.has = XMLWrapper_has;\r
+XMLListWrapper.prototype.get = XMLWrapper_get;\r
+XMLListWrapper.prototype.val = XMLWrapper_val;\r
+XMLListWrapper.prototype.find = XMLWrapper_find;\r
\r
\r
/**\r
- * 通信のキャンセル 通信中の場合は停止&破棄する kill(), busy() メソッドだけを持つ。X.Pair によって、プライベートメンバは隠蔽される。X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_ERROR, X_EVENT_TIMEOUT, X_EVENT_CANCELED\r
- * 通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。\r
- * SUCCESS, ERROR, TIMEOUT イベント以降は kill() は無視される。X.Net インスタンスは COMPLETE で自動で破棄される。\r
+ * <p>busy() メソッドだけを持つ。通信用のプロパティは X.Pair によって隠蔽される。\r
+ * <h4>通信のキャンセル</h4>\r
+ * <p>kill() で通信待ち中はキャンセル&破棄、通信中の場合は通信の中断&破棄を行う。SUCCESS, ERROR, TIMEOUT イベント以降は kill() は無視される。\r
+ * <h4>イベント</h4>\r
+ * <dl>\r
+ * <dt>X.Event.PROGRESS<dd>通信進行状況\r
+ * <dt>X.Event.SUCCESS<dd>通信成功\r
+ * <dt>X.Event.ERROR<dd>通信エラー\r
+ * <dt>X.Event.TIMEOUT<dd>通信タイムアウト\r
+ * <dt>X.Event.CANCELED<dd>通信のユーザー、プログラムによるキャンセル\r
+ * <dt>X.Event.COMPLETE<dd>通信完了。SUCCESS, ERROR, TIMEOUT, CANCELED 後に発生。\r
+ * </dl>\r
+ * <p>X.Net インスタンスは COMPLETE 後に自動で破棄される。\r
+ * <h4>必須プロパティ</h4>\r
+ * <dl>\r
+ * <dt>url<dd>URL\r
+ * <dt>type<dd>'xhr', 'jsonp', 'image', 'img'\r
+ * <dt>xhr<dd>URL { url : 'hoge', type : 'xhr' } の省略形\r
+ * <dt>jsonp<dd>URL { url : 'hoge', type : 'jsonp' } の省略形\r
+ * <dt>image, img<dd>URL { url : 'hoge', type : 'image' } の省略形\r
+ * </dl>\r
+ * <h4>XHR 用プロパティ</h4>\r
+ * <dl>\r
+ * <dt>method<dd>'GET', 'POST' 未指定かつ postdata を設定している場合、'POST' になる。\r
+ * <dt>postdata<dd>string, object の場合は X.String.serialize される。\r
+ * <dt>async<dd>boolean\r
+ * <dt>username<dd>BASIC 認証\r
+ * <dt>password<dd>BASIC 認証\r
+ * <dt>headers<dd>object xhr.setRequestHeader する値\r
+ * <dt>timeout<dd>タイムアウト ms\r
+ * <dt>cache<dd>headers[ 'Pragma' ] = 'no-cache' 等を設定するか?\r
+ * <dt>dataType<dd>'text', 'json', 'xml', 'blob', 'arraybuffer' 等。xhr.responseType に指定する値\r
+ * <dt>mimeType<dd>'text/xml', 'audio/mpeg' 等。xhr.overrideMimeType する値\r
+ * <dt>auth<dd>X.OAuth2 インスタンス(OAuth2 サービスの定義)\r
+ * </dl>\r
+ * \r
* @alias X.Net\r
* @class 各種ネットワーク機能をラップしインターフェイスを共通化する。\r
* @constructs Net\r
'X.Net',\r
X_Class.NONE,\r
{\r
+ 'netType' : '',\r
+ 'netName' : '',\r
+ 'netVersion' : 0,\r
+ \r
'Constructor' : function( urlOrObject, opt_options ){\r
var v, opt, url, type;\r
\r
switch( X_NET_currentData.netType ){\r
case X_NET_TYPE_XHR :\r
\r
- // TODO xProtocol, method -> gadget.io.makeRequset, flash\r
- \r
+ // TODO (xProtocol | method) & canUse -> gadget.io.makeRequset, flash\r
+ // force 'gadget', 'flash'\r
X_NET_currentWrapper = X_NET_XHRWrapper || X_TEMP.X_Net_XHR_init();\r
\r
// OAuth2\r
IE9 で 画像バイナリの取得 VBA をかましている\r
http://web.archive.org/web/20130808105151/http://gurimmer.lolipop.jp/daihakken/2012/05/22/javascriptajaxxmlhttprequest%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Fajax%E3%81%AE%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89\r
http://d.hatena.ne.jp/maachang/20130221/1361427565\r
+\r
+http://web.archive.org/web/20130531162446/http://gurimmer.lolipop.jp/daihakken/2012/06/25/ajaxjavascript%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96xmlhttp%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E3%81%AF/\r
+\r
+IE8 以下で xhr の失敗率が高い問題 \r
+http://tkengo-totoro.blogspot.jp/2011/11/iexmlhttprequest.html\r
+TODO クライアント側にもリトライ機構を入れてみる\r
+\r
*/\r
var // Opera7.6+, Safari1.2+, khtml3.?+, Gecko0.9.7+\r
// ie7&8 ではローカルリソースには ActiveX の XHR を使う\r
X_Net_XHR_createXDR = window[ 'XDomainRequest' ] && function(){ return X_Net_XHR_xdr || ( X_Net_XHR_xdr = new XDomainRequest() ); },\r
X_Net_XHR_xdr = X_Net_XHR_createXDR && X_Net_XHR_createXDR(),\r
\r
- // ie11の互換モード(7,8,5)の msxml はいまいち動かない\r
+ // ie11の互換モード(7,8)の msxml はいまいち動かない\r
X_Net_XHR_createMSXML = X_UA[ 'ActiveX' ] && ( X_UA[ 'IE5x' ] || X_UA[ 'IE6' ] || X_URL_IS_LOCAL ) &&\r
( new Function( 'f', [\r
'var x=".XMLHTTP",',\r
X_Net_XHR_createMSXML = null;\r
};\r
\r
-X[ 'Net' ][ 'XHR' ] = {\r
+X[ 'XHR' ] = {\r
+\r
+ 'W3C' : X_Net_XHR_createW3C ? 1 : 0,\r
+ 'MSXML' : X_Net_XHR_createMSXML ? 2 : 0,\r
+ 'XDR' : X_Net_XHR_createXDR ? 4 : 0,\r
\r
/*\r
* http://hakuhin.jp/as/import.html\r
* http://hakuhin.jp/as/javascript.html\r
* Flash から JavaScript にアクセスする(3+)\r
*/\r
- 'FLASH' : false,\r
+ 'FLASH' : X_Pulgin_FLASH_ENABLED && 4 <= X_Pulgin_FLASH_VERSION ? 8 : 0,\r
+ \r
+ 'GADGET' : 5.5 <= X_UA[ 'IE' ] || !X_UA[ 'IE' ] ? 16 : 0,\r
\r
/**\r
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest\r
* Progress Events Chrome7, firefox3.5, ie10, opera12, Safari?, Chrome for Android 0.16\r
*/\r
- 'PROGRESS' : X_Net_XHR_progress,\r
+ 'PROGRESS' : X_Net_XHR_progress,\r
\r
- 'UL_PROGRESS' : X_Net_XHR_upload,\r
+ 'UPLOAD_PROGRESS' : X_Net_XHR_upload,\r
\r
- // or gadget proxy\r
- 'CORS' : X_Net_XHR_xdr || ( X_Net_XHR_w3c && X_Net_XHR_w3c.withCredentials !== undefined )\r
+ // or gadget proxy or flash\r
+ 'CORS' : X_Net_XHR_xdr || ( X_Net_XHR_w3c && X_Net_XHR_w3c.withCredentials !== undefined )\r
};\r
\r
+if( X_Net_XHR_msXMLVer ) X[ 'XHR' ][ 'MSXML_VERSION' ] = X_Net_XHR_msXMLVer;\r
+\r
if( X_Net_XHR_w3c || X_Net_XHR_msXML ){\r
\r
X_TEMP.X_Net_XHR_init = function(){\r
this._dataType = obj[ 'dataType' ] || X_URL_getEXT( url );\r
\r
if( !raw || xDomain !== this._isXDR || ( X_Net_XHR_createMSXML && isFile !== this._isMsXML ) ){\r
- this[ 'unlisten' ]( [ 'load', 'readystatechange', 'progress', 'error', 'timeout' ] );\r
+ raw && this[ 'unlisten' ]( [ 'load', 'readystatechange', 'progress', 'error', 'timeout' ] );\r
init = true;\r
this[ '_rawObject' ] = raw = xDomain ?\r
X_Net_XHR_createXDR() :\r
};\r
\r
if( !this._isXDR && ( this._isMsXML ? 3 <= X_Net_XHR_msXMLVer : raw.setRequestHeader ) ){ // msxml は setRequestHeader getter がいけない\r
+ \r
+ /*\r
+ if( noCache ){\r
+ headers[ 'Pragma' ] = 'no-cache';\r
+ headers[ 'Cache-Control' ] = 'no-cache';\r
+ headers[ 'If-Modified-Since' ] = 'Thu, 01 Jun 1970 00:00:00 GMT';\r
+ } else */ \r
// http://nakigao.sitemix.jp/blog/?p=2040\r
// json 取得時に SafariでHTTP/412のエラー。但し相手が audio の場合、この指定があるとロードに失敗する。 iOS8.2, iOS7.1 では遭遇せず\r
if( this._dataType === 'json' ){\r
if( method === 'POST' && !headers[ 'Content-Type' ] ){\r
headers[ 'Content-Type' ] = 'application/x-www-form-urlencoded';\r
};\r
- /*\r
- if( noCache ){\r
- headers[ 'Pragma' ] = 'no-cache';\r
- headers[ 'Cache-Control' ] = 'no-cache';\r
- headers[ 'If-Modified-Since' ] = 'Thu, 01 Jun 1970 00:00:00 GMT';\r
- }; */\r
+\r
\r
for( p in headers ){\r
if( X_EMPTY_OBJECT[ p ] ) continue;\r
},\r
\r
cancel : function(){\r
- /* X.Net.XHR.CANCELABLE && */ this[ '_rawObject' ].abort && this[ '_rawObject' ].abort();\r
+ /* this[ '_rawObject' ].abort && */ this[ '_rawObject' ].abort();\r
this._canceled = true;\r
},\r
\r
// XMLHttpRequest の使い方\r
// http://webos-goodies.jp/archives/50548720.html\r
// XMLHttpRequest オブジェクトを再利用する際も、 abort メソッドを呼び出す必要があるようです。\r
- this[ '_rawObject' ].abort && this[ '_rawObject' ].abort(); \r
+ /* this[ '_rawObject' ].abort && */ this[ '_rawObject' ].abort(); \r
\r
// XMLHttpRequest で順番にリソースを取得する\r
// http://note.chiebukuro.yahoo.co.jp/detail/n16248\r
http://www.semblog.org/msano/archives/000407.html\r
* */ \r
case 'readystatechange' :\r
- //if( !X.Net.XHR.PROGRESS ){\r
+ //if( !X.XHR.PROGRESS ){\r
switch( raw.readyState ){\r
case 0 :\r
case 1 :\r
--- /dev/null
+/*\r
+ * gadgets.io.makeRequest\r
+ * \r
+ * 1. gadget-iframe を作る。指示を # で渡す。 元文書は frame 内の images の監視を開始する。\r
+ * 1. 通信用 img の src\r
+ *\r
+ * 2. gadget-iframe が 通信用 img を作る。#ready\r
+ * \r
+ * 3. 元文書が #ready を受け取ったら、iframe の # を書き換えて指示を送る。指示が長い場合、分割して送る。\r
+ * \r
+ * 4. gadget-iframe は 通信用 img の # に結果を書く。コンテンツが長い場合、分割する。\r
+ * \r
+ * 5. 元文書は結果を受け取ったことを gadget-iframe の # に書いて伝える。\r
+ * \r
+ * \r
+ */\r
+\r
+\r
+\r
+var X_NET_GIMR_canUse = 5.5 <= X_UA[ 'IE' ] || !X_UA[ 'IE' ],\r
+ \r
+ X_NET_GIMR_iframeName = 'gadgetProxy_' + ( Math.random() * 100000 | 0 ),\r
+ \r
+ X_NET_GIMR_GADGET_XML_URL = 'http://googledrive.com/host/0B4Y86MXyTfuoVUkwTE54T3V1V1U',\r
+ \r
+ X_NET_GIMR_GADGET_URL = 'http://www.ig.gmodules.com/gadgets/ifr?url=' + encodeURIComponent( X_NET_GIMR_GADGET_XML_URL ) + '&nocache=1',\r
+ \r
+ X_NET_GIMR_IMAGE_URL = 'img/opacity0.gif',\r
+ \r
+ X_NET_GIMR_detection = new Function( 'f,j,i', 'for(j=f.length;j;)try{i=f[--j];return i.location.hash}catch(e){}' ),\r
+ \r
+ X_NET_GIMR_gadgetIframe,\r
+ \r
+ X_NET_GIMR_requestOptions,\r
+ \r
+ X_NET_GIMR_requestOriginal,\r
+ \r
+ X_NET_GIMR_timerID,\r
+ \r
+ X_NET_GIMR_phase = 0,\r
+ \r
+ X_NET_GIMR_lastHashString;\r
+\r
+\r
+function X_NET_GIMR_detectImageOverIframe(){\r
+ var raw = X_NET_GIMR_gadgetIframe[ '_rawObject' ],\r
+ iwin, ret;\r
+ \r
+ if( raw ){\r
+ iwin = raw.contentWindow || ( raw.contentDocument && raw.contentDocument.parentWindow ) || window.frames[ X_NET_GIMR_iframeName ];\r
+ \r
+ if( iwin && iwin.frames && iwin.frames.length ){\r
+ ret = X_NET_GIMR_detection( iwin.frames );\r
+ if( ret && ret !== X_NET_GIMR_lastHashString ){\r
+ X_NET_GIMR_lastHashString = ret;\r
+ console.log( ret );\r
+ \r
+ switch( X_NET_GIMR_phase ){\r
+ case 0 : // init\r
+ // TODO 分割\r
+ iwin.location.href = X_NET_GIMR_GADGET_URL + '#' + encodeURIComponent( X_NET_GIMR_toJSONString( X_NET_GIMR_requestOptions ) );\r
+ break;\r
+ \r
+ case 1 : // after makeRequest > :ok 待ち\r
+ iwin.location.href = X_NET_GIMR_GADGET_URL + '#_waiting_';\r
+ break;\r
+\r
+ case 2 : // _waiting_ 通信結果待ち\r
+ ret = decodeURIComponent( ret.substr( 1 ) );\r
+ ret = window.JSON ? JSON.parse( ret ) : eval( '(' + ret + ')' );\r
+ \r
+ X_NET_GIMRWrapper._busy = false;\r
+ \r
+ X_NET_GIMRWrapper\r
+ [ 'asyncDispatch' ]( {\r
+ type : ret[ 'errors' ] && ret[ 'errors' ].length ? X_EVENT_ERROR : X_EVENT_SUCCESS,\r
+ data : ret\r
+ } );\r
+ iwin.location.href = X_NET_GIMR_GADGET_URL + '#_recived_';\r
+ X_NET_GIMR_timerID = X_NET_GIMR_phase = 0;\r
+ X_NET_GIMR_lastHashString = '';\r
+ \r
+ return X_Callback_UN_LISTEN;\r
+ };\r
+ ++X_NET_GIMR_phase;\r
+ };\r
+ };\r
+ };\r
+};\r
+\r
+// コマンドが長い場合、分割する\r
+function X_NET_GIMR_toJSONString( obj ){\r
+ var json = '', k, v;\r
+ for( k in obj ){\r
+ if( json ) json += ',';\r
+ v = obj[ k ];\r
+ v = v || v === 0 ? v : null;\r
+ json += '"' + k + '":' + ( X_Type_isObject( v ) ? X_NET_GIMR_toJSONString( v ) : X_Type_isString( v ) ? '"' + v + '"' : v );\r
+ };\r
+ console.log( json );\r
+ return '{' + json + '}';\r
+};\r
+\r
+\r
+// TODO extend NinjaIframe\r
+X_NET_GIMRWrapper = X_Class_override(\r
+ X_EventDispatcher(),\r
+ {\r
+\r
+ _busy : false,\r
+ _canceled : false,\r
+ _onloadCount : 0,\r
+ \r
+ load : function( obj ){\r
+ var k;\r
+ //createURL\r
+ if( !X_NET_GIMR_gadgetIframe ){\r
+ X_NET_GIMR_gadgetIframe = X_Node_systemNode\r
+ .create( 'iframe', {\r
+ className : 'hidden-iframe',\r
+ name : X_NET_GIMR_iframeName,\r
+ id : X_NET_GIMR_iframeName,\r
+ src : X_NET_GIMR_GADGET_URL + '#' + encodeURIComponent(\r
+ X_NET_GIMR_toJSONString( { 'img' : X_URL_toAbsolutePath( X_NET_GIMR_IMAGE_URL ), 'len' : 1000, 'itvl' : 200 } ) ),\r
+ scrolling : 'no',\r
+ allowtransparency : 'no', \r
+ frameborder : 0,\r
+ tabindex : -1\r
+ } );\r
+ };\r
+ \r
+ X_NET_GIMR_timerID = X.Timer.add( 100, 0, X_NET_GIMR_detectImageOverIframe );\r
+ \r
+ X_NET_GIMR_requestOriginal = X_Object_deepCopy( v );\r
+ \r
+ X_NET_GIMR_requestOptions = {\r
+ 'CONTENT_TYPE' : 'TEXT',\r
+ 'GET_FULL_HEADERS' : true,\r
+ 'REFRESH_INTERVAL' : 0\r
+ };\r
+ \r
+ for( k in obj ){\r
+ if( v = obj[ k ] ){\r
+ switch( k ){\r
+ case 'postdata' :\r
+ X_NET_GIMR_requestOptions[ 'POST_DATA' ] = v;\r
+ break;\r
+ case 'method' :\r
+ X_NET_GIMR_requestOptions[ 'METHOD' ] = v;\r
+ break; \r
+ case 'dataType' :\r
+ X_NET_GIMR_requestOptions[ 'CONTENT_TYPE' ] = v;\r
+ break;\r
+ case 'headers' :\r
+ X_NET_GIMR_requestOptions[ 'HEADERS' ] = X_Object_clone( v );\r
+ break;\r
+ case 'cashe' :\r
+ X_NET_GIMR_requestOptions[ 'REFRESH_INTERVAL' ] = 3600;\r
+ break;\r
+ }; \r
+ };\r
+ };\r
+ \r
+ this._busy = true;\r
+ },\r
+ \r
+ cancel : function(){\r
+ this._canceled = true;\r
+ },\r
+ \r
+ reset : function(){\r
+ this._busy = this._canceled = false;\r
+ this._onloadCount = 0;\r
+ }\r
+ }\r
+);\r
--- /dev/null
+
+var X_NET_OAUTH2_detection = new Function( 'w', 'try{return w.location.search}catch(e){}' ),
+ X_NET_OAUTH2_authorizationWindow,
+ X_NET_OAUTH2_authorizationTimerID;
+
+/**
+ * イベント
+ * <dl>
+ * <dt>X.Event.NEED_AUTH<dd>window を popup して認可を行う必要あり。ポインターイベント内で oauth2.requestAuth() を呼ぶ。
+ * <dt>X.Event.CANCELED<dd>認可 window が閉じられた。([x]等でウインドウが閉じられた、oauth2.cancelAuth() が呼ばれた)
+ * <dt>X.Event.SUCCESS<dd>認可 window でユーザーが認可し、続いてコードの認可が済んだ。
+ * <dt>X.Event.ERROR<dd>コードの認可のエラー、リフレッシュトークンのエラー、ネットワークエラー
+ * <dt>X.Event.PROGRESS<dd>コードを window から受け取った、リフレッシュトークンの開始、コードの認可を header -> params に切替
+ * </dl>
+ *
+ * OAuth2 state
+ * <dl>
+ * <dt>0 : <dd>disconnected
+ * <dt>1 : <dd>now authentication ...
+ * <dt>+ : <dd>authorization_code
+ * <dt>2 : <dd>refresh_token
+ * <dt>3 : <dd>hasAccessToken
+ * </dl>
+ */
+X[ 'OAuth2' ] = X_EventDispatcher[ 'inherits' ](
+ 'X.OAuth2',
+ X_Class.NONE,
+
+ /** @lends OAuth2.prototype */
+ {
+ 'Constructor' : function( obj ){
+
+ X_Pair_create( this, obj = X_Object_clone( obj ) );
+
+ if( _getAccessToken( this ) ){
+ obj.oauth2State = 3;
+ } else {
+ this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ }
+
+ obj.onAuthError = X_NET_OAUTH2_onXHR401Error;
+ obj.updateRequest = X_NET_OAUTH2_updateRequest;
+ },
+
+ 'authState' : function(){
+ return X_Pair_get( this ).oauth2State || 0;
+ },
+
+ 'requestAuth' : function(){
+ // pointer event 内で呼ぶこと
+ // 二つ以上の popup を作らない
+ if( X_NET_OAUTH2_authorizationWindow ) return;
+
+ pair = X_Pair_get( this );
+
+ if( pair.net || pair.oauth2State ) return;
+
+ X_NET_OAUTH2_authorizationWindow = window.open(
+ pair[ 'authorizeEndpoint' ] + '?' + X_URL_objToParam(
+ {
+ response_type : 'code',
+ client_id : pair[ 'clientID' ],
+ redirect_uri : tpair[ 'redirectURI' ],
+ scope : ( pair[ 'scopes' ] || []).join(' ')
+ }
+ ), 'oauthauthorize',
+ 'width=' + pair[ 'authorizeWindowWidth' ]
+ + ',height=' + pair[ 'authorizeWindowHeight' ]
+ + ',left=' + (screen.width - pair[ 'authorizeWindowWidth' ] ) / 2
+ + ',top=' + (screen.height - pair[ 'authorizeWindowHeight' ] ) / 2
+ + ',menubar=no,toolbar=no');
+
+ X_NET_OAUTH2_authorizationTimerID = X_Timer_add( 333, 0, this, X_Net_OAuth2_detectAuthPopup );
+
+ pair.oauth2State = 1;
+
+ this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to auth.' } );
+ },
+
+ 'cancelAuth' : function(){
+ pair = X_Pair_get( this );
+
+ if( pair.net ){
+ pair.net[ 'kill' ]();
+ delete pair.net;
+ };
+
+ X_NET_OAUTH2_authorizationWindow && X_NET_OAUTH2_authorizationWindow.close();
+ X_NET_OAUTH2_authorizationWindow = null;
+
+ X_NET_OAUTH2_authorizationTimerID && X_Timer_remove( X_NET_OAUTH2_authorizationTimerID );
+ X_NET_OAUTH2_authorizationTimerID = 0;
+
+ this[ 'asyncDispatch' ]( X_EVENT_CANCELED );
+ },
+
+ 'refreshToken' : function(){
+ /*
+ * var expires_at = this._getAccessTokenExpiry();
+ if (expires_at && Date.now() + millis > expires_at)
+ this._refreshAccessToken({replay: false});
+ */
+
+ pair = X_Pair_get( this );
+
+ if( pair.net ) return;
+
+ pair.oauth2State = 2;
+
+ pair.net = X.Net( {
+ 'xhr' : pair[ 'tokenEndpoint' ],
+ 'postdata' : X_URL_objToParam({
+ 'client_id' : pair[ 'clientID' ],
+ 'client_secret' : pair[ 'clientSecret' ],
+ 'grant_type' : 'refresh_token',
+ 'refresh_token' : _getRefreshToken( this )
+ }),
+ 'dataType' : 'json',
+ 'headers' : {
+ 'Accept' : 'application/json',
+ 'Content-Type' : 'application/x-www-form-urlencoded'
+ }
+ } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], this, X_Net_OAuth2_responceHandler );
+
+ this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Start to refresh token.' } );
+ }
+ }
+ );
+
+function X_Net_OAuth2_detectAuthPopup(){
+ var closed, search, pair;
+
+ if( window.frames[ 'oauthauthorize' ] !== X_NET_OAUTH2_authorizationWindow || X_NET_OAUTH2_authorizationWindow.closed ){
+ pair.oauth2State = 0;
+ closed = true;
+ this[ 'asyncDispatch' ]( X_EVENT_CANCELED );
+ } else
+ if( search = X_NET_OAUTH2_detection( X_NET_OAUTH2_authorizationWindow ) ){
+ pair = X_Pair_get( this );
+ pair.code = X_URL_ParamToObj( search.slice( 1 ) )[ 'code' ];
+
+ X_NET_OAUTH2_authorizationWindow.close();
+ closed = true;
+
+ X_Net_OAuth2_authorizationCode( this, pair );
+
+ this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Get code success, then authorization code.' } );
+ };
+
+ if( closed ){
+ X_NET_OAUTH2_authorizationWindow = null;
+ X_NET_OAUTH2_authorizationTimerID = 0;
+
+ return X_Callback_UN_LISTEN;
+ };
+};
+
+function X_Net_OAuth2_authorizationCode( oauth2, pair ){
+ pair.net = X.Net( {
+ 'xhr' : pair[ 'tokenEndpoint' ],
+ 'postdata' : X_URL_objToParam({
+ 'client_id' : pair[ 'clientID' ],
+ 'client_secret' : pair[ 'clientSecret' ],
+ 'grant_type' : 'authorization_code',
+ 'code' : pair.code,
+ 'redirect_uri' : pair[ 'redirectURI' ]
+ }),
+ 'dataType' : 'json',
+ 'headers' : {
+ 'Accept' : 'application/json',
+ 'Content-Type' : 'application/x-www-form-urlencoded'
+ }
+ } ).listenOnce( [ X_EVENT_SUCCESS, X_EVENT_ERROR ], oauth2, X_Net_OAuth2_responceHandler );
+};
+
+function X_Net_OAuth2_responceHandler( e ){
+ var data = e.data,
+ pair = X_Pair_get( this ),
+ isRefresh = pair.oauth2State === 2;
+
+ delete pair.net;
+
+ switch( e.type ){
+ case X_EVENT_SUCCESS :
+ if( isRefresh && data.error ){
+ _removeRefreshToken( this );
+ pair.oauth2State = 0;
+ this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' + data.error, data : data } );
+ this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ return;
+ } else
+ if( data.error ){
+ pair.oauth2State = 0;
+ this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Get new access token error.' + data.error, data : data } );
+ this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ return;
+ };
+
+ _setAccessToken( this, data[ 'access_token' ] || '' );
+ _setRefreshToken( this, data[ 'refresh_token' ] || '' );
+
+ if( data[ 'expires_in' ] ){
+ _setAccessTokenExpiry( this, X_Timer_now() + data[ 'expires_in' ] * 1000 );
+ } else
+ if( _getAccessTokenExpiry( this ) ){
+ _removeAccessTokenExpiry( this );
+ };
+
+ pair.oauth2State = 3;
+ this[ 'asyncDispatch' ]( { type : X_EVENT_SUCCESS, message : isRefresh ? 'Refresh access token success.' : 'Get new access token success.' } );
+ break;
+
+ case X_EVENT_ERROR :
+ if( isRefresh ){
+ // other error, not auth
+ pair.oauth2State = 0;
+ this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'Refresh access token error.' } );
+ _removeRefreshToken( this );
+ this[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ } else
+ if( _getAuthMechanism( this ) === 'param' ){
+ pair.oauth2State = 0;
+ this[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'network-error' } );
+ } else {
+ pair.oauth2State = 0;
+ _setAuthMechanism( 'param' );
+ this[ 'asyncDispatch' ]( { type : X_EVENT_PROGRESS, message : 'Refresh access token failed. retry header -> param. ' } );
+ // retry
+ X_Net_OAuth2_authorizationCode( this, pair );
+ };
+ break;
+ };
+};
+
+function X_NET_OAUTH2_onXHR401Error( oauth2 ){
+ var pair = this,
+ xhr, bearerParams, headersExposed = false;
+
+ if( _getAuthMechanism( oauth2 ) !== 'param' ){
+ xhr = X_NET_currentWrapper[ '_rawObject' ];
+ bearerParams = xhr.getResponseHeader( 'WWW-Authenticate' );
+ headersExposed = !X_Net_XHR_X_DOMAIN || !!xhr.getAllResponseHeaders(); // this is a hack for Firefox and IE
+ };
+
+ // http://d.hatena.ne.jp/ritou/20110402/1301679908
+ if ( bearerParams && bearerParams.indexOf( ' error="' ) === -1 ) {
+ pair.oauth2State = 0;
+ oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ } else
+ if ((( bearerParams && bearerParams.indexOf( ' error="invalid_token"' ) !== -1 ) || !headersExposed) && _getRefreshToken( oauth2 ) ) {
+ _removeAccessToken( oauth2 ); // It doesn't work any more.
+ pair.oauth2State = 2;
+ oauth2.refreshToken();
+ } else
+ if (!headersExposed && !_getRefreshToken( oauth2 )) {
+ pair.oauth2State = 0;
+ oauth2[ 'asyncDispatch' ]( X_EVENT_NEED_AUTH );
+ };
+};
+
+function X_NET_OAUTH2_updateRequest( oauth2, request ){
+ var token = _getAccessToken( oauth2 ),
+ mechanism = _getAuthMechanism( oauth2 ),
+ url = request[ 'url' ],
+ headers;
+
+ if( token && mechanism === 'param'){
+ request[ 'url' ] = url + ((url.indexOf('?') !== -1) ? '&' : '?') + 'bearer_token=' + encodeURIComponent( token );
+ };
+
+ if( token && ( !mechanism || mechanism === 'header' ) ){
+ request[ 'headers' ] || ( headers = request[ 'headers' ] = {} );
+ headers[ 'Authorization' ] = 'Bearer ' + token;
+ };
+};
+
+function _getAccessToken( that ){ return updateLocalStorage( '', that, 'accessToken' ); }
+function _getRefreshToken( that){ return updateLocalStorage( '', that, 'refreshToken' ); }
+function _getAccessTokenExpiry( that ){ return updateLocalStorage( '', that, 'tokenExpiry' ); }
+function _getAuthMechanism( that ){
+ // IE's XDomainRequest doesn't support sending headers, so don't try.
+ return X_Net_XHR_X_DOMAIN ? 'param' : updateLocalStorage( '', that, 'AuthMechanism' );
+ }
+function _setAccessToken( that, value ){ updateLocalStorage( '+', that, 'accessToken' , value); }
+function _setRefreshToken( that, value ){ updateLocalStorage( '+', that, 'refreshToken', value); }
+function _setAccessTokenExpiry( that, value ){ updateLocalStorage( '+', that, 'tokenExpiry', value); }
+function _setAuthMechanism( that, value ){ updateLocalStorage( '+', that, 'AuthMechanism', value); }
+
+function _removeAccessToken( that ){ updateLocalStorage( '-', that, 'accessToken' ); }
+function _removeRefreshToken( that ){ updateLocalStorage( '-', that, 'refreshToken' ); }
+function _removeAccessTokenExpiry( that ){ updateLocalStorage( '-', that, 'tokenExpiry' ); }
+function _removeAuthMechanism( that ){ updateLocalStorage( '-', that, 'AuthMechanism' ); }
+
+function updateLocalStorage( cmd, that, name, value ){
+ var action = cmd === '+' ? 'setItem' : '-' ? 'removeItem' : 'getItem',
+ pair;
+
+ if( window.localStorage ){
+ return window.localStorage[ action ]( X_Pair_get( that )[ 'clientID' ] + name, value );
+ };
+
+ pair = X_Pair_get( that );
+ switch( cmd ){
+ case '+' :
+ pair[ name ] = value;
+ break;
+ case '-' :
+ if( pair[ name ] !== undefined ) delete pair[ name ];
+ };
+ return pair[ name ];
+};
+
QuickTime : 8,\r
*/\r
\r
-var X_Audio_BACKENDS = [], // Array.<Hash>\r
- X_Audio_WRAPPER_LIST = []; // Array.<AudioWrapper>\r
-\r
-function X_Audio_getAudioWrapper( proxy ){\r
- var i = X_Audio_WRAPPER_LIST.length;\r
- for( ; i; ){\r
- if( X_Audio_WRAPPER_LIST[ --i ].proxy === proxy ) return X_Audio_WRAPPER_LIST[ i ];\r
- };\r
-};\r
+var X_Audio_BACKENDS = []; // Array.<Hash>\r
\r
/*\r
* X_EVENT_BACKEND_READY\r
* X_EVENT_MEDIA_SEEKING シーク中に音声が待機状態に。間もなく X_EVENT_MEDIA_PLAYING に移行。\r
*/\r
\r
+// TODO この内容は、AudioBackend の Abstract クラスにする。AudioSprite は Audio ではなく AudioBackend をマネージする\r
X[ 'Audio' ] = X_EventDispatcher[ 'inherits' ](\r
'X.Audio',\r
X_Class.POOL_OBJECT,\r
{\r
'source' : '',\r
'backendName' : '',\r
- _backend : -1,\r
\r
'Constructor' : function( sourceList, opt_option ){\r
X_Audio_startDetectionBackend(\r
},\r
\r
'play' : function( startTime, endTime, loop, loopStartTime, loopEndTime ){\r
- var state, duration;\r
- if( 0 <= startTime ){\r
- this[ 'state' ]( {\r
- currentTime : startTime,\r
- startTime : startTime,\r
- endTime : endTime,\r
- loop : loop,\r
- loopStartTime : loopStartTime,\r
- loopEndTime : loopEndTime\r
- } );\r
- };\r
- this._backend !== -1 && X_Audio_getAudioWrapper( this ).play();\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.play( startTime, endTime, loop, loopStartTime, loopEndTime );\r
return this;\r
},\r
\r
'seek' : function( seekTime ){\r
- var state = this[ 'state' ](),\r
- end = X_AudioWrapper_getEndTime( X_Audio_getAudioWrapper( this ) );\r
- if( seekTime < end ){\r
- this[ 'state' ]( { currentTime : seekTime } );\r
- };\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.seek( seekTime );\r
return this;\r
},\r
\r
'pause' : function(){\r
- this[ 'state' ]().playing && X_Audio_getAudioWrapper( this ).pause();\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.pause();\r
return this;\r
},\r
\r
'state' : function( obj ){\r
- var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
-\r
+ var pair = X_Pair_get( this );\r
if( obj === undefined ){\r
- return backend ?\r
- backend.state() :\r
+ return pair ? pair.getState() :\r
{\r
'startTime' : -1,\r
'endTime' : -1,\r
'looded' : false,\r
'error' : false,\r
'playing' : false,\r
- \r
'source' : this[ 'source' ] || '',\r
'duration' : 0\r
};\r
};\r
- backend && backend.state( obj );\r
+ pair && pair.setState( obj );\r
return this;\r
}, \r
\r
'loop' : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
- if( v === undefined ){\r
- return backend && backend.state().loop;\r
- };\r
- backend && backend.state( { loop : v } );\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.loop( v );\r
return this;\r
},\r
\r
'volume' : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
- if( v === undefined ){\r
- return backend && backend.state().volume;\r
- };\r
- backend && backend.state( { volume : v } );\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.volume( v );\r
return this;\r
},\r
\r
'currentTime' : function( v ){\r
- var backend = this._backend !== -1 && X_Audio_getAudioWrapper( this );\r
- if( v === undefined ){\r
- return backend && backend.state().currentTime;\r
- };\r
- backend && backend.state( { currentTime : v } );\r
+ var pair = X_Pair_get( this );\r
+ pair && pair.currentTime( v );\r
return this;\r
},\r
\r
'isPlaying' : function(){\r
- return this._backend !== -1 && X_Audio_getAudioWrapper( this ).state().playing;\r
+ var pair = X_Pair_get( this );\r
+ return pair && pair.playing;\r
}\r
\r
}\r
);\r
\r
function X_Audio_handleEvent( e ){\r
+ var backend;\r
+ \r
switch( e.type ){\r
case X_EVENT_BACKEND_READY :\r
+ backend = X_Audio_BACKENDS[ e[ 'backendID' ] ];\r
+ \r
this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_Audio_handleEvent );\r
- this[ 'source' ] = e.source;\r
- this[ 'backendName' ] = X_Audio_BACKENDS[ this._backend ].backendName;\r
- X_Audio_WRAPPER_LIST.push(\r
- new X_Audio_BACKENDS[ this._backend ]\r
- .klass( this, e.source, e.option ) );\r
+ this[ 'source' ] = e[ 'source' ];\r
+ this[ 'backendName' ] = backend.backendName;\r
+ X_Pair_create( this, backend.klass( this, e[ 'source' ], e[ 'option' ] ) );\r
break;\r
\r
case X_EVENT_BACKEND_NONE :\r
break;\r
\r
case X_EVENT_KILL_INSTANCE :\r
- this._backend !== -1 && X_Audio_getAudioWrapper( this ).close();\r
+ backend = X_Pair_get( this );\r
+ backend && backend[ 'kill' ]();\r
+ X_Pair_release( this, backend );\r
break;\r
};\r
};\r
* TODO preplayerror play してみたら error が出た、backend の変更。\r
*/\r
\r
-function X_Audio_startDetectionBackend( backend, proxy, sourceList, option ){\r
+function X_Audio_startDetectionBackend( backend, xaudio, sourceList, option ){\r
var source = sourceList[ 0 ] || '', \r
ext = X_URL_getEXT( source ),\r
sup;\r
\r
if( source && backend ){\r
- sup = [ proxy, sourceList, option, source, ext ];\r
+ sup = [ xaudio, sourceList, option, source, ext ];\r
sup[ 5 ] = sup;\r
\r
- proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup );\r
- backend.detect( proxy, source, ext );\r
+ xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, backend, X_Audio_onEndedDetection, sup );\r
+ backend.detect( xaudio, source, ext );\r
} else {\r
- proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+ xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
};\r
};\r
\r
-function X_Audio_onEndedDetection( e, proxy, sourceList, option, source, ext, sup ){\r
+function X_Audio_onEndedDetection( e, xaudio, sourceList, option, source, ext, sup ){\r
var i = X_Audio_BACKENDS.indexOf( this ), backend;\r
\r
if( e.canPlay ){\r
- proxy._backend = i;\r
- proxy[ 'asyncDispatch' ]( {\r
+ xaudio._backend = i;\r
+ xaudio[ 'asyncDispatch' ]( {\r
type : X_EVENT_BACKEND_READY,\r
'option' : option,\r
'source' : source,\r
- 'backendName' : this[ 'backendName' ]\r
+ 'backendName' : this[ 'backendName' ],\r
+ 'backendID' : i\r
} ); \r
} else {\r
console.log( 'No ' + source + ' ' + this[ 'backendName' ] );\r
if( sup[ 3 ] = source = sourceList[ sourceList.indexOf( source ) + 1 ] ){\r
sup[ 4 ] = ext = X_URL_getEXT( source );\r
- proxy[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup );\r
- this.detect( proxy, source, ext );\r
+ xaudio[ 'listenOnce' ]( X_EVENT_COMPLETE, this, X_Audio_onEndedDetection, sup );\r
+ this.detect( xaudio, source, ext );\r
} else\r
if( backend = X_Audio_BACKENDS[ i + 1 ] ){\r
- X_Audio_startDetectionBackend( backend, proxy, sourceList, option );\r
+ X_Audio_startDetectionBackend( backend, xaudio, sourceList, option );\r
} else {\r
- proxy[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+ xaudio[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
}; \r
};\r
};\r
\r
\r
\r
-function X_AudioWrapper_updateStates( audioWrapper, obj ){\r
- var playing = audioWrapper.playing,\r
- k, v,\r
- end = 0, seek = 0, volume = 0;\r
- \r
- for( k in obj ){\r
- v = obj[ k ];\r
- switch( k ){\r
- case 'currentTime' :\r
- v = X_AudioWrapper_timeStringToNumber( v );\r
- if( X_Type_isNumber( v ) ){\r
- if( playing ){\r
- if( audioWrapper.state().currentTime !== v ){\r
- audioWrapper.seekTime = v;\r
- seek = 2;\r
+var X_Audio_AbstractAudioBackend = X_EventDispatcher[ 'inherits' ](\r
+ 'X.AbstractAudioBackend',\r
+ X_Class.ABSTRACT,\r
+ {\r
+ \r
+ url : '',\r
+ target : null,\r
+ \r
+ startTime : 0,\r
+ endTime : -1,\r
+ loopStartTime : -1,\r
+ loopEndTime : -1,\r
+ seekTime : -1,\r
+ duration : 0,\r
+\r
+ playing : false,\r
+ error : 0, \r
+ autoLoop : false,\r
+ looped : false,\r
+ autoplay : false,\r
+ gain : 0.5,\r
+ \r
+ play : function( startTime, endTime, loop, loopStartTime, loopEndTime ){\r
+ if( 0 <= startTime ){\r
+ this.setState( {\r
+ currentTime : startTime,\r
+ startTime : startTime,\r
+ endTime : endTime,\r
+ loop : loop,\r
+ loopStartTime : loopStartTime,\r
+ loopEndTime : loopEndTime\r
+ } );\r
+ };\r
+ this.actualPlay();\r
+ },\r
+ \r
+ seek : function( seekTime ){\r
+ if( seekTime < X_AudioWrapper_getEndTime( this ) ){\r
+ this.setState( { currentTime : seekTime } );\r
+ };\r
+ },\r
+ \r
+ pause : function(){\r
+ this.playing && this.actualPause();\r
+ }, \r
+ \r
+ loop : function( v ){\r
+ if( v === undefined ){\r
+ return this.autoLoop;\r
+ };\r
+ this.setState( { loop : v } );\r
+ },\r
+\r
+ volume : function( v ){\r
+ if( v === undefined ){\r
+ return this.gain;\r
+ };\r
+ this.setState( { volume : v } );\r
+ },\r
+\r
+ currentTime : function( v ){\r
+ if( v === undefined ){\r
+ return this.playing ? this.getActualCurrentTime() : this.seekTime;\r
+ };\r
+ this.setState( { currentTime : v } );\r
+ },\r
+ \r
+ getState : function(){\r
+ \r
+ return {\r
+ 'startTime' : this.startTime,\r
+ 'endTime' : this.endTime < 0 ? this.duration : this.endTime,\r
+ 'loopStartTime' : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
+ 'loopEndTime' : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
+ 'loop' : this.autoLoop,\r
+ 'looped' : this.looped,\r
+ 'volume' : this.gain,\r
+ 'playing' : this.playing, \r
+ 'duration' : this.duration,\r
+ \r
+ 'currentTime' : this.playing ? this.getActualCurrentTime() : this.seekTime,\r
+ 'error' : this.getActualError ? this.getActualError() : this.error\r
+ };\r
+ },\r
+ \r
+ setState : function( obj ){\r
+ var playing = this.playing,\r
+ k, v,\r
+ end = 0, seek = 0, volume = 0;\r
+ \r
+ for( k in obj ){\r
+ v = obj[ k ];\r
+ switch( k ){\r
+ case 'currentTime' :\r
+ v = X_AudioWrapper_timeStringToNumber( v );\r
+ if( X_Type_isNumber( v ) ){\r
+ if( playing ){\r
+ if( this.getActualCurrentTime() !== v ){\r
+ seek = 2;\r
+ this.seekTime = v;\r
+ }; \r
+ } else {\r
+ this.seekTime = v;\r
+ };\r
+ } else {\r
+ continue;\r
};\r
- } else {\r
- audioWrapper.seekTime = v;\r
- };\r
- } else {\r
- continue;\r
- };\r
- break;\r
+ break;\r
+ \r
+ case 'startTime' :\r
+ v = X_AudioWrapper_timeStringToNumber( v );\r
+ if( v || v === 0 ){\r
+ if( this.startTime !== v ){\r
+ this.startTime = v; \r
+ };\r
+ } else {\r
+ delete this.startTime;\r
+ };\r
+ break;\r
\r
- case 'startTime' :\r
- case 'endTime' :\r
- case 'loopStartTime' :\r
- case 'loopEndTime' :\r
- v = X_AudioWrapper_timeStringToNumber( v );\r
- console.log( k + ' ' + v );\r
- if( v || v === 0 ){\r
- if( audioWrapper[ k ] !== v ){\r
- audioWrapper[ k ] = v;\r
+ case 'endTime' :\r
+ v = X_AudioWrapper_timeStringToNumber( v );\r
+ if( v || v === 0 ){\r
+ if( this.endTime !== v ){\r
+ this.endTime = v;\r
+ if( playing ) end = 1; \r
+ };\r
+ } else {\r
+ delete this.endTime;\r
+ if( playing ) end = 1;\r
+ };\r
+ break;\r
\r
- // 再生中の endTime の変更\r
- if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1; \r
- };\r
- } else {\r
- delete audioWrapper[ k ];\r
- if( playing && ( k === 'endTime' || k === 'loopEndTime' ) ) end = 1;\r
- };\r
- break;\r
-\r
- case 'looped' :\r
- if( playing ) seek = 2;\r
- case 'loop' :\r
- case 'autoplay' :\r
- if( X_Type_isBoolean( v ) && audioWrapper[ k ] !== v ){\r
- audioWrapper[ k ] = v;\r
+ case 'loopStartTime' :\r
+ v = X_AudioWrapper_timeStringToNumber( v );\r
+ if( v || v === 0 ){\r
+ if( this.loopStartTime !== v ){\r
+ this.loopStartTime = v; \r
+ };\r
+ } else {\r
+ delete this.loopStartTime;\r
+ };\r
+ break;\r
+ \r
+ case 'loopEndTime' :\r
+ v = X_AudioWrapper_timeStringToNumber( v );\r
+ if( v || v === 0 ){\r
+ if( this.loopEndTime !== v ){\r
+ this.loopEndTime = v;\r
+ if( playing ) end = 1; \r
+ };\r
+ } else {\r
+ delete this.loopEndTime;\r
+ if( playing ) end = 1;\r
+ };\r
+ break;\r
+ \r
+ case 'looped' :\r
+ if( X_Type_isBoolean( v ) && this.looped !== v ){\r
+ this.looped = v;\r
+ if( playing ) seek = 2;\r
+ };\r
+ break;\r
+ \r
+ case 'loop' :\r
+ if( X_Type_isBoolean( v ) && this.autoLoop !== v ){\r
+ this.autoLoop = v;\r
+ };\r
+ break;\r
+ \r
+ case 'autoplay' :\r
+ if( X_Type_isBoolean( v ) && this.autoplay !== v ){\r
+ this.autoplay = v;\r
+ };\r
+ break;\r
+ \r
+ case 'volume' :\r
+ if( X_Type_isNumber( v ) ){\r
+ v = v < 0 ? 0 : 1 < v ? 1 : v;\r
+ if( this.gain !== v ){\r
+ this.gain = v;\r
+ // if playing -> update\r
+ if( playing ) volume = 4;\r
+ };\r
+ };\r
+ break;\r
};\r
- break;\r
+ };\r
+ \r
+ if( this.endTime < this.startTime ||\r
+ ( this.loopEndTime < 0 ? this.endTime : this.loopEndTime ) < ( this.loopStartTime < 0 ? this.startTime : this.loopStartTime ) ||\r
+ X_AudioWrapper_getEndTime( this ) < this.seekTime// ||\r
+ //this.duration < this.endTime\r
+ ){\r
+ console.log( 'setState 0:' + this.startTime + ' -> ' + this.endTime + ' d:' + this.duration + ' 1:' + this.loopStartTime + ' -> ' + this.loopEndTime );\r
+ return;\r
+ };\r
+ \r
+ v = end + seek + volume;\r
+ return v && this.afterUpdateState( v ); \r
+ }\r
+ \r
+ }\r
+);\r
\r
- case 'volume' :\r
- if( X_Type_isNumber( v ) ){\r
- v = v < 0 ? 0 : 1 < v ? 1 : v;\r
- if( audioWrapper[ k ] !== v ){\r
- audioWrapper[ k ] = v;\r
- // if playing -> update\r
- if( playing ) volume = 4;\r
- };\r
- };\r
- break;\r
- };\r
- };\r
- \r
- if( audioWrapper.endTime < audioWrapper.startTime ||\r
- ( audioWrapper.loopEndTime < 0 ? audioWrapper.endTime : audioWrapper.loopEndTime ) < ( audioWrapper.loopStartTime < 0 ? audioWrapper.startTime : audioWrapper.loopStartTime ) ||\r
- X_AudioWrapper_getEndTime( audioWrapper ) < audioWrapper.seekTime// ||\r
- //audioWrapper.duration < audioWrapper.endTime\r
- ){\r
- console.log( 'error @updateStateObject() begin:' + audioWrapper.startTime + ' end:' + audioWrapper.endTime + ' d:' + audioWrapper.duration + ' ls:' + audioWrapper.loopStartTime );\r
- return 0;\r
- };\r
- \r
- return end + seek + volume;\r
-};\r
\r
function X_AudioWrapper_timeStringToNumber( time ){\r
var ary, ms, s = 0, m = 0, h = 0;\r
var X_Audio_WebAudio_context = !X_UA[ 'iPhone_4s' ] && !X_UA[ 'iPad_2Mini1' ] && !X_UA[ 'iPod_4' ] &&
!( X_UA[ 'Gecko' ] && X_UA[ 'Android' ] ) &&
( window.AudioContext || window.webkitAudioContext ),
+ X_Audio_BUFFER_LIST = [],
X_Audio_WebAudioWrapper;
/*
X_Audio_WebAudio_context = new X_Audio_WebAudio_context;
- function X_Audio_WebAudio_getBuffer( url ){
- var i = 0, l = X_Audio_WRAPPER_LIST.length;
- for( i = 0; i < l; ++i ){
- if( X_Audio_WRAPPER_LIST[ i ].url === url ) return X_Audio_WRAPPER_LIST[ i ];
- };
- };
-
- X_Audio_WebAudioWrapper = X_EventDispatcher[ 'inherits' ](
- 'X.AV.WebAudioWrapper',
+ X_Audio_BufferLoader = X_EventDispatcher[ 'inherits' ](
+ 'X.AV.WebAudioBufferLoader',
X_Class.POOL_OBJECT,
{
-
url : '',
- proxy : null,
-
- startTime : 0,
- endTime : -1,
- loopStartTime : -1,
- loopEndTime : -1,
- seekTime : -1,
- duration : 0,
-
- playing : false,
- error : 0,
- loop : false,
- looped : false,
- autoplay : false,
- volume : 0.5,
-
- _startPos : 0,
- _endPosition : 0,
- _startTime : 0,
- _timerID : 0,
- _interval : 0,
- buffer : null,
- source : null,
- gainNode : null,
- _onended : null,
-
xhr : null,
onDecodeSuccess : null,
onDecodeError : null,
- Constructor : function( proxy, url, option ){
- var audio = X_Audio_WebAudio_getBuffer( url );
-
- this.proxy = proxy;
- this.url = url;
-
- X_AudioWrapper_updateStates( this, option );
-
- if( audio && audio.buffer ){
- this._onDecodeSuccess( audio.buffer );
- } else
- if( audio ){
- // TODO 当てにしていたaudioがclose 等した場合
- audio.proxy[ 'listenOnce' ]( 'canplaythrough', this, this._onBufferReady );
- } else {
- this.xhr = X.Net( { 'xhr' : url, 'type' : 'arraybuffer' } )
+ buffer : null,
+ error : 0,
+ webAudioList : null,
+
+ Constructor : function( webAudio, url ){
+ this.webAudioList = [ webAudio ];
+ this.url = url;
+ this.xhr = X.Net( { 'xhr' : url, 'dataType' : 'arraybuffer' } )
[ 'listen' ]( X_EVENT_PROGRESS, this )
- [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );
- };
+ [ 'listenOnce' ]( [ X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
},
handleEvent : function( e ){
switch( e.type ){
case X_EVENT_PROGRESS :
- e[ 'percent' ] ?
- this.proxy[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } ) :
- this.proxy[ 'dispatch' ]( 'loadstart' );
+ this[ 'dispatch' ]( { type : 'progress', 'percent' : e[ 'percent' ] } );
return;
case X_EVENT_SUCCESS :
- console.log( 'WebAudio xhr success! ' + !!X_Audio_WebAudio_context.decodeAudioData + ' t:' + typeof e.data );
// TODO 旧api
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Porting_webkitAudioContext_code_to_standards_based_AudioContext
};
break;
- case X_EVENT_CANCELED :
- this.error = 1;
- this.proxy[ 'dispatch' ]( 'aborted' );
- break;
-
case X_EVENT_COMPLETE :
- this.error = 2;
- this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'xhr error' } );
+ this.error = 1;
+ this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
break;
};
- this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE, X_EVENT_CANCELED ], this );
+ this.xhr[ 'unlisten' ]( [ X_EVENT_PROGRESS, X_EVENT_SUCCESS, X_EVENT_COMPLETE ], this );
delete this.xhr;
},
this.onDecodeSuccess && this._onDecodeComplete();
if ( !buffer ) {
- this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'buffer is ' + buffer } );
+ this.error = 2;
+ this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
return;
};
this.buffer = buffer;
- this.duration = buffer.duration * 1000;
- /*
- this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );
- this.proxy[ 'asyncDispatch' ]( 'loadeddata' );
- this.proxy[ 'asyncDispatch' ]( 'canplay' );
- this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );
- */
- this.proxy[ 'asyncDispatch' ]( X_EVENT_READY );
-
- this.autoplay && X_Timer_once( 16, this, this.play );
-
+
+ this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
+
console.log( 'WebAudio decoded!' );
},
_onDecodeError : function(){
console.log( 'WebAudio decode error!' );
this._onDecodeComplete();
- this.error = 3;
- this.proxy[ 'asyncDispatch' ]( { type : X_EVENT_ERROR, message : 'decode error' } );
+ this.error = 2;
+ this[ 'asyncDispatch' ]( X_EVENT_COMPLETE );
},
_onDecodeComplete : function(){
X_Callback_correct( this.onDecodeError );
delete this.onDecodeError;
},
-
- _onBufferReady : function( e ){
- var audio = X_Audio_WebAudio_getBuffer( this.url );
- this._onDecodeSuccess( audio.buffer );
- },
- close : function(){
- delete this.buffer;
-
- if( this.xhr ) this.xhr.close();
-
- if( this.onDecodeSuccess ){
- // 回収はあきらめる、、、
+ unregister : function( webAudio ){
+ var list = this.webAudioList,
+ i = list.indexOf( webAudio );
+ if( 0 < i ){
+ list.splice( i, 1 );
+ if( list.length ){
+ this.xhr && this.xhr[ 'kill' ]();
+ this[ 'kill' ]();
+ };
};
+ }
+
+ }
+ );
- this.playing && this.pause();
- this.source && this._sourceDispose();
- this._onended && X_Callback_correct( this._onended );
-
- this.gainNode && this.gainNode.disconnect();
- },
+ X_Audio_WebAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](
+ 'X.AV.WebAudioWrapper',
+ X_Class.POOL_OBJECT,
+ {
- _sourceDispose : function(){
- this.source.disconnect();
- delete this.source.onended;
- delete this.source;
- },
+ loader : null,
+
+ _startPos : 0,
+ _endPosition : 0,
+ _startTime : 0,
+ _timerID : 0,
+ _interval : 0,
+ buffer : null,
+ source : null,
+ gainNode : null,
+ _onended : null,
+
+ Constructor : function( target, url, option ){
+ var i = 0,
+ l = X_Audio_BUFFER_LIST.length,
+ loader;
+
+ for( ; i < l; ++i ){
+ loader = X_Audio_BUFFER_LIST[ i ];
+ if( loader.url === url ){
+ this.loader = loader;
+ loader.webAudioList.push( this );
+ break;
+ };
+ };
+
+ if( !this.loader ){
+ this.loader = loader = new X_Audio_BufferLoader( this, url );
+ };
+
+ this.target = target || this;
+
+ this.setState( option );
+
+ this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_WebAudio_handleEvent );
+
+ if( loader.buffer || loader.error ){
+ this._onLoadBufferComplete();
+ } else {
+ loader[ 'listenOnce' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+ };
+ },
+ _onLoadBufferComplete : function( e ){
+ var loader = this.loader,
+ buffer = loader.buffer;
+
+ e && loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete );
+
+ if ( !buffer ) {
+ this.error = loader.error;
+
+ this.target[ 'dispatch' ]({
+ type : X_EVENT_ERROR,
+ error : loader.error,
+ message : loader.error === 1 ?
+ 'load buffer network error' :
+ 'buffer decode error'
+ });
+ this[ 'kill' ]();
+ return;
+ };
+
+ this.buffer = buffer;
+ this.duration = buffer.duration * 1000;
+
+ this.target[ 'asyncDispatch' ]( X_EVENT_READY );
+
+ this.autoplay && X_Timer_once( 16, this, this.play );
+
+ },
- play : function(){
+ actualPlay : function(){
var begin, end;
if( !this.buffer ){
this.source.buffer = this.buffer;
this.source.connect( this.gainNode );
- this.gainNode.gain.value = this.volume;
+ this.gainNode.gain.value = this.gain;
// おかしい、stop 前に外していても呼ばれる、、、@Firefox33.1
// 破棄された X.Callback が呼ばれて、obj.proxy() でエラーになる。Firefox では、onended は使わない
this._startTime = X_Audio_WebAudio_context.currentTime * 1000;
this._interval = this._interval || X_Timer_add( 1000, 0, this, this._onInterval );
},
+
+ _sourceDispose : function(){
+ this.source.disconnect();
+ delete this.source.onended;
+ delete this.source;
+ },
_onInterval : function(){
if( !this.playing ){
delete this._interval;
return X_Callback_UN_LISTEN;
};
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );
},
_onEnded : function(){
};
};
- if( this.loop ){
- if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
+ if( this.autoLoop ){
+ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){
this.looped = true;
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
- this.play();
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );
+ this.actualPlay();
};
} else {
- this.pause();
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
+ this.actualPause();
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );
};
};
},
- pause : function(){
+ actualPause : function(){
if( !this.playing ) return this;
console.log( '[WebAudio] pause' );
- this.seekTime = this.state().currentTime;
+ this.seekTime = this.getActualCurrentTime();
this._timerID && X_Timer_remove( this._timerID );
delete this._timerID;
this.source.stop( 0 ) : this.source.noteOff( 0 );
};
},
-
- state : function( obj ){
- var result;
-
- if( obj === undefined ){
- return {
- startTime : this.startTime,
- endTime : this.endTime < 0 ? this.duration : this.endTime,
- loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,
- loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,
- loop : this.loop,
- looped : this.looped,
- volume : this.volume,
- playing : this.playing,
- duration : this.duration,
-
- currentTime : this.playing ? ( X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0 ) : this.seekTime,
- error : this.error
- };
- };
- result = X_AudioWrapper_updateStates( this, obj );
-
+ getActualCurrentTime : function(){
+ return X_Audio_WebAudio_context.currentTime * 1000 - this._startTime + this._startPos | 0;
+ },
+
+ afterUpdateState : function( result ){
if( result & 2 || result & 1 ){ // seek
- this.play();
+ this.actualPlay();
} else
if( result & 4 ){
- this.gainNode.gain.value = this.volume;
+ this.gainNode.gain.value = this.gain;
};
}
}
);
+ function X_WebAudio_handleEvent( e ){
+ switch( e.type ){
+
+ case X_EVENT_KILL_INSTANCE :
+ this.loader[ 'unlisten' ]( X_EVENT_COMPLETE, this, this._onLoadBufferComplete )
+ .unregister( this );
+
+ delete this.buffer;
+
+ this.playing && this.actualPause();
+ this.source && this._sourceDispose();
+
+ this._onended && X_Callback_correct( this._onended );
+
+ this.gainNode && this.gainNode.disconnect();
+ break;
+ };
+ };
+
/*
* http://qiita.com/sou/items/5688d4e7d3a37b4e2ff1
* L-01F 等の一部端末で Web Audio API の再生結果に特定条件下でノイズが混ざることがある。
X_UA[ 'iOS' ] ? 'suspend' :\r
X_UA[ 'AndroidBrowser2' ] ? 'stalled' : // Android 2.3.5(SBM101SH) では stalled は発生しない,,,\r
X_UA[ 'AndroidBrowser4' ] ? 'loadeddata' : \r
- X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : 'canplay',\r
+ X_UA[ 'OperaMobile' ] || X_UA[ 'OperaTablet' ] ? 'loadeddata' : 'loadeddata', //'canplay',\r
X_Audio_HTMLAudioWrapper,\r
X_Audio_constructor = window[ 'Audio' ] || window.HTMLAudioElement,\r
X_Audio_rawAudio,\r
X_Audio_HTMLAudioWrapper_ieMobile9Fix = ( X_UA[ 'WinPhone' ] && X_UA[ 'IE9' ] ),\r
X_Audio_HTMLAudioWrapper_durationFix = ( !X_Audio_HTMLAudioWrapper_currentTimeFix && 12 <= X_UA[ 'Opera' ] ),\r
\r
- X_Audio_HTMLAudioWrapper_shortPlayFix = X_UA[ 'AndroidBrowser2' ],\r
+ X_Audio_HTMLAudioWrapper_shortPlayFix = //X_UA[ 'AndroidBrowser2' ],\r
+ X_UA[ 'AndroidBrowser' ] && X_UA[ 'AndroidBrowserWebkit' ] < 534.3, // Android 4.1.1 でも遭遇\r
\r
X_Audio_codecs;\r
\r
};\r
};\r
\r
- X_Audio_HTMLAudioWrapper = X_EventDispatcher[ 'inherits' ](\r
+ X_Audio_HTMLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](\r
'X.AV.HTML5AudioWrapper',\r
X_Class.POOL_OBJECT,\r
{\r
- \r
- proxy : null,\r
- \r
- startTime : 0,\r
- endTime : -1,\r
- loopStartTime : -1,\r
- loopEndTime : -1,\r
- seekTime : -1,\r
- duration : 0,\r
- \r
- playing : false,\r
- error : 0, \r
- loop : false,\r
- looped : false,\r
- autoplay : false,\r
- volume : 0.5,\r
-\r
_playTime : 0,\r
_closed : true,\r
_loaded : false,\r
_lastCurrentTime : 0,\r
_src : '',\r
\r
- Constructor : function( proxy, source, option ){\r
+ Constructor : function( target, source, option ){\r
var raw;\r
\r
- this.proxy = proxy;\r
+ this.target = target || this;\r
this._closed = false;\r
\r
- X_AudioWrapper_updateStates( this, option );\r
+ this.setState( option );\r
\r
if( option[ 'useVideo' ] ){\r
- this[ '_rawObject' ] = raw = document.createElement( 'video' );\r
+ this[ '_rawObject' ] = raw = document.createElement( 'video' );\r
raw.preload = 'none'; // auto, metadata, none\r
//raw.autoplay = false, // no-auto\r
- raw.loop = false;\r
- raw.muted = false;\r
- //raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え\r
+ raw.loop = false;\r
+ raw.muted = false;\r
+ raw.crossorigin = option[ 'crossorigin' ] || ''; //crossorigin: "anonymous", X.URL.isSameDomain() で切り替え\r
raw.style.cssText = 'position:absolute;bottom:0;left:-50px;width:100px;height:100px;opacity:0;';\r
raw.controls = false;\r
raw.WebKitPlaysInline = true;\r
raw.src = source;\r
- //raw.onclick = "alert('play');this.play();";\r
+ //raw.onclick = "alert('play');this.actualPlay();";\r
document.body.appendChild( raw );\r
//raw.load();\r
} else {\r
switch( e.type ){\r
\r
case X_EVENT_KILL_INSTANCE :\r
+ // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】\r
+ // http://ingaouhou.com/archives/3633\r
+ // ・使い終わったインスタンスはload()しておくとやや安定\r
+ this.playing && this.actualPause();\r
+ delete this._closed;\r
+ delete this._loaded;\r
+ \r
+ this[ '_rawObject' ].src = '';\r
+ this[ '_rawObject' ].load();\r
+ \r
+ // removeChild for video\r
break;\r
};\r
},\r
\r
X_Audio_HTMLAudioWrapper_badOperaAndroid && alert( e.type );\r
\r
- console.log( e.type );\r
+ //console.log( e.type );\r
\r
switch( e.type ){\r
case 'loadstart' : // ブラウザがコンテンツの検索を開始した場合に発生\r
break;\r
\r
case 'ended' :\r
- if( !this._closed && this.loop ){\r
- if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+ if( !this._closed && this.autoLoop ){\r
+ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
this.looped = true;\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
- this.play();\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+ this.actualPlay();\r
};\r
return;\r
};\r
if( !this.duration && X_Type_isFinite( this[ '_rawObject' ].duration ) ){\r
this.duration = this.duration || this[ '_rawObject' ].duration * 1000;\r
this._playForDuration = 2;\r
- \r
- //this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
- //this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
- //this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
- //this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );\r
loaded = true;\r
//console.log( 'durationFix が完了' + this.duration );\r
break;\r
};\r
} else\r
if( this[ '_rawObject' ].currentTime === this._lastCurrentTime ){\r
- //this.proxy[ 'dispatch' ]( 'seeking' );\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+ //this.target[ 'dispatch' ]( 'seeking' );\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
return;\r
};\r
this._lastCurrentTime = this[ '_rawObject' ].currentTime;\r
\r
if( this.playing ){\r
end = X_AudioWrapper_getEndTime( this );\r
- now = X_Audio_HTMLAudioWrapper_currentTimeFix ? X_Timer_now() - this._playTime + this._beginTime : this[ '_rawObject' ].currentTime * 1000 | 0;\r
+ now = this.getActualCurrentTime();\r
+ //console.log( end + ' / ' + now );\r
if( 0 + end <= 0 + now ){ // なぜか iem9 で必要,,,\r
- if( this.loop ){\r
- if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+ if( this.autoLoop ){\r
+ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
this.looped = true;\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
- this.play();\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+ this.actualPlay();\r
};\r
} else {\r
- this.pause();\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ this.actualPause();\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
};\r
return;\r
};\r
console.log( '設定 ' + this._beginTime );\r
return;\r
};\r
- \r
- /*\r
- this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
- this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
- this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
- this.proxy[ 'asyncDispatch' ]( 'canplaythrough' ); */\r
- \r
- \r
+\r
loaded = true;\r
console.log( 'durationFix が完了' + this.duration );\r
\r
if( !this._loaded && ( loaded || e.type === X_Audio_HTMLAudio_playTrigger || e.type === 'loadeddata' ) ){\r
this.autoplay && X_Timer_once( 16, this, this.play );\r
this._loaded = true;\r
- this.proxy[ 'dispatch' ]( X_EVENT_READY );\r
+ this.target[ 'asyncDispatch' ]( X_EVENT_READY );\r
console.log( 'Loaded! ' + e.type + ' d:' + ( this.duration | 0 ) );\r
return;\r
};\r
\r
- loaded || ( type && this.proxy[ 'dispatch' ]( type ) );\r
- },\r
- \r
- close : function(){\r
- // 【javascript】モバイル向けブラウザでも音を鳴らしたい【WebAudio】\r
- // http://ingaouhou.com/archives/3633\r
- // ・使い終わったインスタンスはload()しておくとやや安定\r
- this.playing && this.pause();\r
- delete this._closed;\r
- delete this._loaded;\r
- \r
- this[ '_rawObject' ].src = '';\r
- this[ '_rawObject' ].load();\r
+ if( !loaded && type ){\r
+ this.target[ 'dispatch' ]( type );\r
+ type === X_EVENT_ERROR && this[ 'kill' ]();\r
+ };\r
},\r
- \r
- play : function(){\r
+\r
+ actualPlay : function(){\r
var begin, end;\r
\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
delete this._playForDuration;\r
};\r
\r
- \r
if( !this.playing ){\r
if( X_UA[ 'Chrome' ] ){ // [CHROME][FIX] volume TODO どの version で 修正される?\r
// [!] delay\r
X_Timer_once( 0, this, this._fixForChrome );\r
this[ '_rawObject' ].volume = 0;\r
} else {\r
- this[ '_rawObject' ].volume = this.volume;\r
+ this[ '_rawObject' ].volume = this.gain;\r
};\r
this[ '_rawObject' ].play();\r
this.playing = true;\r
\r
// [CHROME][FIX] volume\r
_fixForChrome : X_UA[ 'Chrome' ] && function(){\r
- !this._closed && ( this[ '_rawObject' ].volume = this.volume );\r
+ !this._closed && ( this[ '_rawObject' ].volume = this.gain );\r
},\r
\r
- pause : function(){\r
+ actualPause : function(){\r
if( !this.playing ) return;\r
\r
- this.seekTime = this.state().currentTime;\r
+ this.seekTime = this.getActualCurrentTime();\r
\r
delete this._playTime;\r
\r
};\r
delete this.playing;\r
},\r
- \r
- state : function( obj ){\r
- var result;\r
- \r
- if( obj === undefined ){\r
- return {\r
- startTime : this.startTime,\r
- endTime : this.endTime < 0 ? this.duration : this.endTime,\r
- loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
- loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
- \r
- loop : this.loop,\r
- looped : this.looped,\r
- volume : this.volume,\r
- playing : this.playing, // && !this[ '_rawObject' ].error && !this[ '_rawObject' ].paused && !this[ '_rawObject' ].ended, \r
- duration : this.duration,\r
- \r
- currentTime :\r
- this.playing ?\r
- ( X_Audio_HTMLAudioWrapper_currentTimeFix ?\r
+ \r
+ getActualCurrentTime : function(){\r
+ return ( 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
- 2 = MEDIA_ERR_NETWORK - error occurred when downloading\r
- 3 = MEDIA_ERR_DECODE - error occurred when decoding\r
- 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\r
- */\r
- error : this[ '_rawObject' ].error || 0 // 0, 1 ~ 4 \r
- }; \r
- };\r
+ this[ '_rawObject' ].currentTime * 1000 | 0 );\r
+ },\r
+ /*\r
+ http://www.w3schools.com/tags/av_prop_error.asp\r
+ 1 = MEDIA_ERR_ABORTED - fetching process aborted by user\r
+ 2 = MEDIA_ERR_NETWORK - error occurred when downloading\r
+ 3 = MEDIA_ERR_DECODE - error occurred when decoding\r
+ 4 = MEDIA_ERR_SRC_NOT_SUPPORTED - audio/video not supported\r
+ */ \r
+ getActualError : function(){\r
+ return this[ '_rawObject' ].error || 0;\r
+ },\r
\r
- result = X_AudioWrapper_updateStates( this, obj );\r
- \r
- if( result & 2 ){ // seek\r
- this.play();\r
- //} else\r
- //if( result & 1 ){\r
- //if( X_Audio_HTMLAudioWrapper_currentTimeFix ){\r
- // this.play(); \r
- //};\r
-\r
+ afterUpdateState : function( result ){\r
+ if( result & 3 ){ // seek\r
+ this.actualPlay();\r
} else\r
if( result & 4 ){\r
- this[ '_rawObject' ].volume = this.volume;\r
- };\r
- \r
+ this[ '_rawObject' ].volume = this.gain;\r
+ }; \r
}\r
\r
}\r
if( X[ 'Pulgin' ][ 'SilverlightEnabled' ] ){\r
\r
// X.Node.inherits はできない。_rawObject は <object> でなく silverlight\r
- X_Audio_SLAudioWrapper = X_EventDispatcher[ 'inherits' ](\r
+ X_Audio_SLAudioWrapper = X_Audio_AbstractAudioBackend[ 'inherits' ](\r
'X.AV.SilverlightAudioWrapper',\r
X_Class.POOL_OBJECT,\r
{\r
- '_rawType' : X_EventDispatcher_EVENT_TARGET_TYPE.SILVER_LIGHT,\r
- proxy : null,\r
- \r
- startTime : 0,\r
- endTime : -1,\r
- loopStartTime : -1,\r
- loopEndTime : -1,\r
- seekTime : -1,\r
- duration : 0,\r
- \r
- playing : false,\r
- error : 0, \r
- loop : false,\r
- looped : false,\r
- autoplay : false,\r
- volume : 0.5,\r
- \r
+ '_rawType' : X_EventDispatcher_EVENT_TARGET_SILVER_LIGHT,\r
+\r
_onload : '',\r
_callback : null, \r
xnodeObject : null,\r
_lastState : '',\r
_interval : 0, // setInterval timer id\r
\r
- Constructor : function( proxy, source, option ){\r
+ Constructor : function( target, source, option ){\r
+ var xnodeScript;\r
\r
if( !X_Audio_SLAudio_uid ){\r
// source\r
- // X_Node_systemNode[ 'create' ]( 'script', { type : 'text/xaml', id : 'silverlightaudio' } )\r
- // [ 'text' ]( '<Canvas xmlns="http://schemas.microsoft.com/client/2007" ' +\r
- // 'xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>');\r
+ //xnodeScript = X_Node_head[ 'create' ]( 'script', { type : 'text/xaml', id : 'silverlightaudio' } );\r
+ //xnodeScript[ '_rawObject' ].innerHTML = '<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas>';\r
\r
// html に以下を書いた \r
// <script id="silverlightaudio" type="text/xaml"><Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"></Canvas></script>\r
*/\r
\r
// TODO embed\r
- this.proxy = proxy;\r
+ this.target = target || this;\r
this._source = source;\r
// X.Audio._slOnload_ は不可\r
this._onload = 'XAudioSilverlightOnLoad' + ( ++X_Audio_SLAudio_uid );\r
//'<param value="true" name="autoUpgrade">' +\r
//'<param name="onerror" value="slerror">' // bond to global\r
);\r
- X_AudioWrapper_updateStates( this, option );\r
+ this.setState( option );\r
\r
this[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE );\r
},\r
\r
- onSLReady : function( sender ){\r
- if( !this._onload ) return;\r
- \r
- window[ this._onload ] = null;\r
- delete this._onload;\r
- X_Callback_correct( this._callback );\r
- delete this._callback;\r
-\r
- sender.children.add(\r
- sender.GetHost().\r
- content.\r
- CreateFromXaml(\r
- '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
- '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.volume + '" AutoPlay="false" />' +\r
- '</Canvas>'));\r
- \r
- this[ '_rawObject' ] = sender.findName('media'); // x:Name='media'\r
-\r
- this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
- },\r
+ onSLReady : function( sender ){\r
+ if( !this._onload ) return;\r
+ \r
+ window[ this._onload ] = null;\r
+ delete this._onload;\r
+ X_Callback_correct( this._callback );\r
+ delete this._callback;\r
+ \r
+ sender[ 'children' ][ 'add' ](\r
+ sender[ 'GetHost' ]()[ 'content' ][ 'CreateFromXaml' ](\r
+ '<Canvas xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">' +\r
+ '<MediaElement x:Name="media" Source="' + this._source + '" Volume="' + this.gain + '" AutoPlay="false" />' +\r
+ '</Canvas>'));\r
+ \r
+ this[ '_rawObject' ] = sender[ 'findName' ]( 'media' ); // x:Name='media'\r
+ \r
+ this[ 'listen' ]( [ 'MediaFailed', 'MediaOpened', 'MediaEnded', 'CurrentStateChanged' ] );\r
+ },\r
\r
handleEvent : function( e ){\r
var lastState, currentState;\r
this.playing = false;\r
this._ended = true;\r
this._paused = false;\r
- this.proxy[ 'dispatch' ]( X_EVENT_ERROR ); // open failed\r
+ if( this.playing ){\r
+ //X_Timer_once( 16, this, this.actualPlay );\r
+ } else {\r
+ this.target[ 'dispatch' ]( X_EVENT_ERROR ); // open failed\r
+ this[ 'kill' ](); \r
+ };\r
break;\r
\r
case 'MediaOpened' :\r
// http://msdn.microsoft.com/ja-jp/library/bb979710(VS.95).aspx\r
- this.duration = this[ '_rawObject' ].NaturalDuration.Seconds * 1000;\r
- // TODO 'canplaythrough'\r
- //this.proxy[ 'asyncDispatch' ]( 'loadstart' );\r
- //this.proxy[ 'asyncDispatch' ]( 'loadedmetadata' );\r
- //this.proxy[ 'asyncDispatch' ]( 'loadeddata' );\r
- //this.proxy[ 'asyncDispatch' ]( 'canplay' );\r
- //this.proxy[ 'asyncDispatch' ]( 'canplaythrough' );\r
- this.proxy[ 'asyncDispatch' ]( X_EVENT_READY );\r
+ this.duration = this[ '_rawObject' ][ 'NaturalDuration' ][ 'Seconds' ] * 1000;\r
+ this.target[ 'asyncDispatch' ]( X_EVENT_READY );\r
\r
- this.autoplay && X_Timer_once( 16, this, this.play );\r
+ this.autoplay && X_Timer_once( 16, this, this.actualPlay );\r
break;\r
\r
- case 'MediaEnded' : \r
- this.loop && this.playing && this.play();\r
+ case 'MediaEnded' :\r
+ //console.log( ' > ' + this.autoLoop + ' error:' + this.error );\r
+ //this.autoLoop && /* this.playing && */ this.actualPlay();\r
+ this._ended = true;\r
break;\r
\r
case 'CurrentStateChanged' :\r
lastState = this._lastState,\r
- currentState = this[ '_rawObject' ].CurrentState;\r
+ currentState = this[ '_rawObject' ][ 'CurrentState' ];\r
\r
// ignore consecutive events or 'Closed' == 'Error'\r
if( lastState === currentState\r
};\r
this._lastState = currentState; // update last state\r
\r
+ console.log( ' > ' + currentState + ' - ' + this._lastUserAction );\r
+ \r
switch( currentState ){\r
case 'Buffering' :\r
case 'Opening' :\r
switch( this._lastUserAction ){\r
case 'play' :\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
break;\r
case 'seek' :\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_SEEKING );\r
break;\r
case 'pause' :\r
break;\r
this.playing = false;\r
this._ended = true;\r
this._paused = false;\r
- this.proxy[ 'dispatch' ]( X_EVENT_ERROR );\r
+ this.target[ 'dispatch' ]( X_EVENT_ERROR );\r
+ this[ 'kill' ]();\r
break;\r
\r
// userAction.pause() -> MediaState('Paused') -> x\r
// userAction.stop() -> MediaState('Paused') -> x\r
// userAction.play() + file end -> MediaState('Paused') -> uueventfire('ended')\r
case 'Paused':\r
- this.playing = false;\r
+ \r
+ this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+ //this.playing = false;\r
\r
switch( this._lastUserAction ){\r
case 'play': // play() -> file end -> event('ended')\r
case 'seek':\r
- this.seekTime = 0;\r
+ //this.seekTime = 0;\r
this._ended = true;\r
this._paused = false;\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
- this._currentTime( this.startTime );\r
+ //this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ //this.setCurrentTime( this.startTime );\r
break;\r
case 'pause':\r
this._ended = false;\r
// media.play -> 'Playing'\r
case 'Playing':\r
this.error = 0;\r
- this.playing = true;\r
+ //this.playing = true;\r
this._ended = false;\r
this._paused = false;\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
break;\r
\r
// stop()\r
case 'Stopped':\r
- this.playing = false;\r
+ this.playing && X_Timer_once( 16, this, this.actualPlay );\r
+ return;\r
+ \r
+ //this.playing = false;\r
this._ended = true;\r
this._paused = false;\r
- this._currentTime( this.startTime );\r
+ //this.setCurrentTime( this.startTime );\r
break;\r
};\r
break;\r
\r
case X_EVENT_KILL_INSTANCE :\r
+ this.playing && this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ this.playing && this.actualPause();\r
+ \r
if( this._onload ){\r
// window への delete に ie5 は対応しないが、そもそも ie5 は Silverlight に非対応\r
window[ this._onload ] = null;\r
};\r
},\r
\r
- close : function(){\r
- this.playing && this.pause();\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
- this[ 'kill' ]();\r
- },\r
- \r
// SilverlightAudio.play\r
- play : function(){\r
- var begin, end;\r
- \r
+ actualPlay : function(){\r
+ var begin, offset, end;\r
+\r
// もし kill 後に autoplayTimer で呼ばれても、_closed==true なので平気\r
if( this.error ) return;\r
if( !this.duration ){\r
\r
end = X_AudioWrapper_getEndTime( this );\r
begin = X_AudioWrapper_getStartTime( this, end, true ) | 0;\r
- \r
- console.log( '[SLAudio] play ' + begin + ' -> ' + end );\r
- \r
- this[ '_rawObject' ].Volume = this.volume;\r
- this._beginTime = begin;\r
- this._currentTime( begin );\r
+\r
+ // 1 秒以下は指定できないため四捨五入\r
+ begin = ( begin / 1000 | 0 ) * 1000 + ( 500 < begin % 1000 ? 1000 : 0 ); \r
+\r
+ this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
\r
- if( !this.playing ){\r
+ this.setCurrentTime( this._beginTime = begin );\r
+ \r
+ console.log( '[play] ' + begin + ' -> ' + end );\r
+ \r
+ /*\r
+ if( offset = begin - this.getActualCurrentTime() ){\r
+ this.setCurrentTime( begin + offset );\r
+ console.log( ' [差補正] ' + offset + ' ct:' + this.getActualCurrentTime() + ' begin:' + begin );\r
+ this._beginTime = begin = this.getActualCurrentTime();\r
+ };*/\r
+ \r
+ if( !this.playing || this._ended ){\r
+ console.log( '[play] play()' + begin + ' -> ' + end );\r
this[ '_rawObject' ].play();\r
- //this.proxy[ 'dispatch' ]( 'play' );\r
- \r
this.playing = true;\r
+ this._ended = false;\r
};\r
\r
this._timerID && X_Timer_remove( this._timerID );\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
+ this._timerID = X_Timer_once( end - begin, this, this._onEnded );\r
\r
if( !this._interval ){\r
this._interval = X_Timer_add( 1000, 0, this, this._onInterval );\r
delete this._interval;\r
return X_Callback_UN_LISTEN;\r
};\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_PLAYING );\r
},\r
\r
_onEnded : function(){\r
delete this._timerID;\r
\r
if( this.playing ){\r
- console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this[ '_rawObject' ].Position.Seconds * 1000 | 0 ) );\r
- time = this[ '_rawObject' ].Position.Seconds * 1000 | 0;\r
+ //console.log( '> end ' + X_AudioWrapper_getEndTime( this ) + ' current:' + ( this.getActualCurrentTime() ) );\r
+ time = this.getActualCurrentTime();\r
\r
- if( time <= this._beginTime ){\r
- console.log( '== waiting' );\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
- this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - this._beginTime, this, this._onEnded );\r
+ if( time < this._beginTime ){\r
+ console.log( '== waiting ' + time + ' < begin:' + this._beginTime );\r
+ this.setCurrentTime( this._beginTime );\r
+ time = this.getActualCurrentTime();\r
+ console.log( ' > ' + time );\r
+ this._ended && this[ '_rawObject' ].play();\r
+ this._ended = false;\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_WAITING );\r
+ this._timerID = X_Timer_once( X_AudioWrapper_getEndTime( this ) - time, this, this._onEnded );\r
return;\r
};\r
\r
time -= X_AudioWrapper_getEndTime( this );\r
if( time < 0 ){\r
- console.log( '> onEnd ' + time );\r
+ console.log( ' > まだ終わらない ' + time );\r
+ this._ended && this[ '_rawObject' ].play();\r
+ this._ended = false;\r
this._timerID = X_Timer_once( -time, this, this._onEnded );\r
return;\r
};\r
\r
- if( this.loop ){\r
- if( !( this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+ if( this.autoLoop ){\r
+ console.log( '========= loop?' );\r
+ if( !( this.target[ 'dispatch' ]( X_EVENT_MEDIA_BEFORE_LOOP ) & X_Callback_PREVENT_DEFAULT ) ){\r
+ console.log( '========== loopした' );\r
this.looped = true;\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
- this.play();\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_LOOPED );\r
+ this.actualPlay();\r
};\r
} else {\r
- this.pause();\r
- this.proxy[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
+ console.log( '========= pause' );\r
+ this.actualPause();\r
+ this.target[ 'dispatch' ]( X_EVENT_MEDIA_ENDED );\r
};\r
};\r
},\r
\r
// SilverlightAudio.pause\r
- pause : function(){\r
+ actualPause : function(){\r
if( this.error || !this.playing ) return;\r
\r
this._lastUserAction = 'pause';\r
- this.seekTime = this.state().currentTime;\r
+ this.seekTime = this.getActualCurrentTime();\r
this.playing = false;\r
this._paused = true;\r
this._ended = false;\r
- \r
+ \r
this[ '_rawObject' ].pause();\r
- //this.proxy[ 'dispatch' ]( 'pause' );\r
+ //this.target[ 'dispatch' ]( 'pause' );\r
},\r
- \r
- // SilverlightAudio.state\r
- state : function( obj ){ // @return Hash: { loop, error, paused, ended, source, duration }\r
- var result, end;\r
- \r
- if( obj === undefined ){\r
- return {\r
- startTime : this.startTime,\r
- endTime : this.endTime < 0 ? this.duration : this.endTime,\r
- loopStartTime : this.loopStartTime < 0 ? this.startTime : this.loopStartTime,\r
- loopEndTime : this.loopEndTime < 0 ? ( this.endTime || this.duration ) : this.loopEndTime,\r
- \r
- // 整数化 しておかないと seek 時に不具合がある。\r
- currentTime : this.playing ? this[ '_rawObject' ].Position.Seconds * 1000 | 0 : this.seekTime,\r
\r
- loop : this.loop,\r
- looped : this.looped,\r
- volume : this.volume,\r
- error : this.error,\r
- playing : this.playing,\r
- duration : this.duration // this[ '_rawObject' ].NaturalDuration.Seconds;\r
- };\r
- };\r
+ getActualCurrentTime : function(){\r
+ return this[ '_rawObject' ][ 'Position' ][ 'Seconds' ] * 1000 | 0;\r
+ },\r
\r
- result = X_AudioWrapper_updateStates( this, obj );\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' ].Position.Seconds * 1000 | 0, this, this._onEnded );\r
- } else {\r
- delete this._timerID;\r
- };\r
-\r
- };\r
- if( result & 4 ){\r
- this[ '_rawObject' ].Volume = this.volume;\r
+ afterUpdateState : function( result ){\r
+ if( result & 3 ){ // seek\r
+ this.actualPlay();\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.getActualCurrentTime(), this, this._onEnded );\r
+ } else {\r
+ delete this._timerID;\r
};\r
- };\r
+ } else\r
+ if( result & 4 ){\r
+ this[ '_rawObject' ][ 'Volume' ] = this.gain;\r
+ }; \r
},\r
\r
// SilverlightAudio.currentTime\r
- _currentTime : function( time ){ // @param Number: time\r
- var position = this[ '_rawObject' ].Position; // [!] create instance\r
+ setCurrentTime : function( time ){ // @param Number: time\r
+ var position = this[ '_rawObject' ][ 'Position' ]; // [!] create instance\r
\r
- position.Seconds = time / 1000 | 0; // set current time\r
+ position[ 'Seconds' ] = time / 1000 | 0; // set current time\r
\r
- this[ '_rawObject' ].Position = position; // [!] reattach instance\r
+ this[ '_rawObject' ][ 'Position' ] = position; // [!] reattach instance\r
}\r
\r
}\r
bgmLooped : false,\r
bgmPlaying : false\r
},\r
- X_Audio_Sprite_instance;\r
+ X_Audio_Sprite_instance,\r
+ X_Audio_Sprite_numTracks,\r
+ X_Audio_Sprite_useVideo;\r
\r
-X[ 'Audio' ][ 'Sprite' ] = {\r
- \r
- 'shouldUse' : X_Audio_Sprite_shouldUse,\r
+X[ 'AudioSprite' ] = function( setting ){\r
+ var tracks = X_Audio_Sprite_TEMP.tracks,\r
+ bgms = X_Audio_Sprite_TEMP.BGMs,\r
+ presets = X_Audio_Sprite_TEMP.presets,\r
+ urls = setting[ 'urls' ],\r
+ video = setting[ 'useVideo' ],\r
+ n = video ? 1 : setting[ 'numTracks' ] || 1,\r
+ option = {\r
+ volume : setting[ 'volume' ] || 0.5,\r
+ autoplay : false,\r
+ startTime : 0,\r
+ endTime : X_Audio_Sprite_lengthSilence,\r
+ loop : true\r
+ },\r
+ k, i, v, track; \r
\r
- 'needTouchFirst' : X_Audio_Sprite_needTouchFirst,\r
+ if( X_Audio_Sprite_instance ){\r
+ X_Audio_Sprite_instance[ 'kill' ]();\r
+ } else {\r
+ X_Audio_Sprite_instance = X_Class_override( X_EventDispatcher(), X_Audio_Sprite_members );\r
+ X_ViewPort[ 'listen' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_Audio_Sprite_instance, X_Audio_Sprite_handleEvent );\r
+ };\r
\r
- 'enableMultiTrack' : X_Audio_Sprite_enableMultiTrack,\r
+ n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks;\r
\r
- 'create' : function( setting ){\r
- // close()\r
- if( X_Audio_Sprite_instance ){\r
- X_Audio_Sprite_instance.close();\r
- } else {\r
- X_Audio_Sprite_instance = X_Class_override( X_EventDispatcher(), X_Audio_Sprite_members );\r
- X_ViewPort[ 'listen' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], X_Audio_Sprite_instance, X_Audio_Sprite_handleEvent );\r
+ for( k in setting ){\r
+ v = setting[ k ];\r
+ if( X_Type_isArray( v ) && v !== urls ){\r
+ v = X_Object_cloneArray( v );\r
+ for( i = v.length; i; ){\r
+ --i;\r
+ if( i !== 2 ) v[ i ] = X_AudioWrapper_timeStringToNumber( v[ i ] );\r
+ }; \r
+ if( v[ 2 ] ) bgms[ k ] = v;\r
+ presets[ k ] = v;\r
};\r
- X_Audio_Sprite_instance.setup( setting );\r
- return X_Audio_Sprite_instance;\r
- \r
- }\r
+ };\r
+ \r
+ X_Audio_startDetectionBackend( X_Audio_BACKENDS[ 0 ], X_Audio_Sprite_instance, X_Object_cloneArray( urls ), option );\r
+\r
+ X_Audio_Sprite_instance[ 'listenOnce' ]( [ X_EVENT_BACKEND_READY, X_EVENT_BACKEND_NONE ], X_AudioSprite_backendHandler );\r
+ X_Audio_Sprite_instance[ 'listenOnce' ]( X_EVENT_KILL_INSTANCE, X_Audio_Sprite_handleEvent );\r
+ \r
+ X_Audio_Sprite_useVideo = video;\r
+ X_Audio_Sprite_numTracks = X_Audio_Sprite_instance[ 'numTracks' ] = n;\r
+\r
+ return X_Audio_Sprite_instance;\r
};\r
\r
+X[ 'AudioSprite' ][ 'shouldUse' ] = X_Audio_Sprite_shouldUse;\r
+X[ 'AudioSprite' ][ 'needTouchFirst' ] = X_Audio_Sprite_needTouchFirst;\r
+X[ 'AudioSprite' ][ 'enableMultiTrack' ] = X_Audio_Sprite_enableMultiTrack;\r
+\r
// 再生が終わっているもの、終わりかけのものを探す\r
// TODO 終わりかけのもの、と一番古いもの、どちらを再利用するか?これ以上に細かい実装を望む場合は X.Audio.Sprite は使わず自力で実装\r
function X_Audio_Sprite_getTrackEnded(){\r
\r
for( ; i < l; ++i ){\r
track = tracks[ i ];\r
- state = track.state();\r
+ state = track.getState();\r
if( !state.playing ) return track;\r
if( track === X_Audio_Sprite_TEMP.bgmTrack ) continue;\r
if( state.currentTime <= X_Audio_Sprite_lengthSilence + X_Audio_Sprite_lengthDistance ) return track;\r
\r
X_Audio_Sprite_members = {\r
\r
- setup : function( setting ){\r
- \r
- var tracks = X_Audio_Sprite_TEMP.tracks,\r
- bgms = X_Audio_Sprite_TEMP.BGMs,\r
- presets = X_Audio_Sprite_TEMP.presets,\r
- urls = setting[ 'urls' ],\r
- video = setting[ 'useVideo' ],\r
- n = video ? 1 : setting[ 'numTracks' ] || 1,\r
- option = {\r
- volume : setting[ 'volume' ] || 0.5,\r
- autoplay : false,\r
- startTime : 0,\r
- endTime : X_Audio_Sprite_lengthSilence,\r
- loop : true\r
- },\r
- k, i, v, track;\r
- \r
- n = n <= X_Audio_Sprite_maxTracks ? n : X_Audio_Sprite_maxTracks;\r
- \r
- for( k in setting ){\r
- v = setting[ k ];\r
- if( X_Type_isArray( v ) && v !== urls ){\r
- v = X_Object_cloneArray( v );\r
- for( i = v.length; i; ){\r
- --i;\r
- if( i !== 2 ) v[ i ] = X_AudioWrapper_timeStringToNumber( v[ i ] );\r
- }; \r
- if( v[ 2 ] ) bgms[ k ] = v;\r
- presets[ k ] = v;\r
- };\r
- };\r
- \r
- for( i = 0; i < n; ++i ){\r
- if( video || ( i === 1 && X_Audio_Sprite_useVideoForMulti ) ){\r
- option[ 'useVideo' ] = true;\r
- };\r
- tracks.push( X[ 'Audio' ]( urls, X_Object_clone( option ) ) );\r
- };\r
- \r
- tracks[ n - 1 ][ 'listenOnce' ]( [ X_EVENT_BACKEND_READY, X_EVENT_BACKEND_NONE ], this, X_Audio_Sprite_handleEvent );\r
- \r
- X_Audio_Sprite_instance.numTracks = n;\r
- },\r
+ 'numTracks' : 0,\r
\r
- close : function(){\r
- var tracks = X_Audio_Sprite_TEMP.tracks,\r
- bgms = X_Audio_Sprite_TEMP.BGMs,\r
- presets = X_Audio_Sprite_TEMP.presets,\r
- k;\r
- \r
- while( tracks.length ){\r
- tracks.pop()[ 'kill' ]();\r
- };\r
- \r
- for( k in bgms ){\r
- delete bgms[ k ];\r
- };\r
- for( k in presets ){\r
- delete presets[ k ];\r
- };\r
- \r
- X_Audio_Sprite_TEMP.bgmTrack = null;\r
- X_Audio_Sprite_TEMP.bgmPosition = 0;\r
- X_Audio_Sprite_TEMP.bgmName = '';\r
- X_Audio_Sprite_TEMP.bgmLooped = false;\r
- X_Audio_Sprite_TEMP.bgmPlaying = false;\r
- },\r
- \r
- load : function(){\r
+ 'load' : function(){\r
var tracks = X_Audio_Sprite_TEMP.tracks,\r
i = 0, l = tracks.length;\r
for( ; i < l; ++i ){\r
if( X_UA[ 'WinPhone' ] ){\r
console.log( 'touch -> play()' );\r
//tracks[ i ].play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 );\r
- this.pause( i );\r
+ this[ 'pause' ]( i );\r
} else {\r
- X_Audio_getAudioWrapper( tracks[ i ] )[ '_rawObject' ].load();\r
+ tracks[ i ][ '_rawObject' ].load();\r
};\r
};\r
},\r
/*\r
* @return uid Number\r
*/\r
- play : function( name ){\r
+ 'play' : function( name ){\r
var bgm = X_Audio_Sprite_TEMP.bgmTrack,\r
tracks = X_Audio_Sprite_TEMP.tracks,\r
bgms = X_Audio_Sprite_TEMP.BGMs,\r
// bgm変更\r
X_Audio_Sprite_TEMP.bgmName = name;\r
X_Audio_Sprite_TEMP.bgmPosition = preset[ 0 ];\r
- X_Audio_Sprite_TEMP.bgmPlaying = true;\r
X_Audio_Sprite_TEMP.bgmLooped = false;\r
};\r
+ \r
+ X_Audio_Sprite_TEMP.bgmPlaying = true;\r
+ \r
if( bgm ){\r
track = bgm;\r
} else\r
track = X_Audio_Sprite_TEMP.bgmTrack = tracks[ 0 ];\r
};\r
\r
- if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).isPlaying() ){\r
- track\r
- .state( {\r
- loop : true,\r
- looped : X_Audio_Sprite_TEMP.bgmLooped,\r
- currentTime : X_Audio_Sprite_TEMP.bgmPosition,\r
- startTime : preset[ 0 ],\r
- endTime : preset[ 1 ],\r
- loopStartTime : preset[ 3 ],\r
- loopEndTime : preset[ 4 ]\r
- } );\r
+ if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).playing ){\r
+ track.setState({\r
+ 'loop' : true,\r
+ 'looped' : X_Audio_Sprite_TEMP.bgmLooped,\r
+ 'currentTime' : X_Audio_Sprite_TEMP.bgmPosition,\r
+ 'startTime' : preset[ 0 ],\r
+ 'endTime' : preset[ 1 ],\r
+ 'loopStartTime' : preset[ 3 ],\r
+ 'loopEndTime' : preset[ 4 ]\r
+ });\r
} else {\r
- track\r
- .state( { looped : X_Audio_Sprite_TEMP.bgmLooped } )\r
- .play( preset[ 0 ], preset[ 1 ], true, preset[ 3 ], preset[ 4 ] )\r
- .seek( X_Audio_Sprite_TEMP.bgmPosition );\r
+ track.setState( { 'looped' : X_Audio_Sprite_TEMP.bgmLooped } );\r
+ track.play( preset[ 0 ], preset[ 1 ], true, preset[ 3 ], preset[ 4 ] );\r
+ track.seek( X_Audio_Sprite_TEMP.bgmPosition );\r
};\r
\r
} else {\r
track = X_Audio_Sprite_getTrackEnded( X_Audio_Sprite_TEMP.bgmPlaying );\r
track\r
[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent )\r
- .state( { looped : false } )\r
- .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
+ .setState( { 'looped' : false } );\r
+ track.play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence );\r
} else {\r
// single track, iOS\r
if( bgm ){\r
X_Audio_Sprite_TEMP.bgmPosition = bgm.currentTime();\r
- console.log( 'bgm position : ' + X_Audio_Sprite_TEMP.bgmPosition + ' isPlay:' + bgm.isPlaying() );\r
+ //console.log( 'bgm position : ' + X_Audio_Sprite_TEMP.bgmPosition + ' isPlay:' + bgm.playing );\r
X_Audio_Sprite_TEMP.bgmTrack = null;\r
};\r
track = tracks[ 0 ];\r
\r
- if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).isPlaying() ){\r
- track\r
- .state( {\r
- loop : true,\r
- looped : false,\r
- startTime : preset[ 0 ],\r
- endTime : preset[ 1 ],\r
- loopStartTime : 0,\r
- loopEndTime : X_Audio_Sprite_lengthSilence\r
- } );\r
+ if( track[ 'listen' ]( [ X_EVENT_MEDIA_PLAYING, X_EVENT_MEDIA_BEFORE_LOOP ], this, X_Audio_Sprite_handleEvent ).playing ){\r
+ track.setState({\r
+ 'loop' : true,\r
+ 'looped' : false,\r
+ //'currentTime' : preset[ 0 ],\r
+ 'startTime' : preset[ 0 ],\r
+ 'endTime' : preset[ 1 ],\r
+ 'loopStartTime' : 0,\r
+ 'loopEndTime' : X_Audio_Sprite_lengthSilence\r
+ });\r
} else {\r
- track\r
- .play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence ); \r
+ track.play( preset[ 0 ], preset[ 1 ], true, 0, X_Audio_Sprite_lengthSilence ); \r
};\r
};\r
};\r
return -1;\r
},\r
\r
- pause : function( uid ){\r
+ 'pause' : function( uid ){\r
var track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
if( X_Audio_Sprite_TEMP.bgmTrack === track ){\r
X_Audio_Sprite_TEMP.bgmPosition = track.currentTime();\r
X_Audio_Sprite_TEMP.bgmPlaying = false;\r
X_Audio_Sprite_TEMP.bgmTrack = null;\r
};\r
- track && track.play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence ).seek( 0 );\r
+ track && track.play( 0, X_Audio_Sprite_lengthSilence, true, 0, X_Audio_Sprite_lengthSilence );\r
+ track && track.seek( 0 );\r
this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PAUSED );\r
return this;\r
},\r
\r
- seek : function( uid, position ){\r
+ 'seek' : function( uid, position ){\r
var track = X_Audio_Sprite_TEMP.tracks[ uid ],\r
end;\r
if( track ){\r
return this;\r
},\r
\r
- volume : function( uid, opt_volume ){\r
+ 'volume' : function( uid, opt_volume ){\r
var track, i;\r
// TODO uid = 0\r
if( uid === 0 ){\r
};\r
track = X_Audio_Sprite_TEMP.tracks[ uid ];\r
if( opt_volume === undefined ){\r
- return track ? track.volume() : -1;\r
+ return track ? track.gain : -1;\r
};\r
track && track.volume( opt_volume );\r
return this;\r
},\r
\r
- state : function( uid, opt_obj ){\r
+ 'state' : function( uid, opt_obj ){\r
var track = X_Audio_Sprite_TEMP.tracks[ uid ],\r
state, start, end;\r
// TODO uid = 0\r
if( opt_obj === undefined ){\r
// TODO pause\r
if( track ){\r
- state = track.state();\r
+ state = track.getState();\r
start = state.startTime;\r
return {\r
- 'currentTime' : state.currentTime - state.startTime,\r
- 'playing' : state.startTime <= state.currentTime && state.currentTime <= state.endTime,\r
- 'duration' : state.endTime - state.startTime,\r
+ 'currentTime' : state.currentTime - start,\r
+ 'playing' : start <= state.currentTime && state.currentTime <= state.endTime,\r
+ 'duration' : state.endTime - start,\r
'volume' : X_Audio_Sprite_TEMP.volume\r
};\r
};\r
return { 'volume' : X_Audio_Sprite_TEMP.volume, 'playing' : false };\r
};\r
- track && track.state( opt_obj );\r
+ track && track.setState( opt_obj );\r
return this;\r
}\r
};\r
\r
-function X_Audio_Sprite_handleEvent( e ){\r
- var i, tracks, track, _e;\r
+function X_AudioSprite_backendHandler( e ){\r
+ var i, backend, option, src, name, last, _e;\r
\r
switch( e.type ){\r
case X_EVENT_BACKEND_READY :\r
+ \r
+ backend = X_Audio_BACKENDS[ e[ 'backendID' ] ];\r
+ option = e[ 'option' ];\r
+ \r
+ this[ 'unlisten' ]( X_EVENT_BACKEND_NONE, X_AudioSprite_backendHandler );\r
+ this[ 'source' ] = src = e[ 'source' ];\r
+ this[ 'backendName' ] = name = backend.backendName;\r
+ \r
+ for( i = 0; i < X_Audio_Sprite_numTracks; ++i ){\r
+ if( X_Audio_Sprite_useVideo || ( i === 1 && X_Audio_Sprite_useVideoForMulti ) ){\r
+ option[ 'useVideo' ] = true;\r
+ };\r
+ // Audiobackend の owner として null を渡すとAudioBackend 自身へ dispatch する\r
+ X_Audio_Sprite_TEMP.tracks.push( last = backend.klass( null, e[ 'source' ], option ) );\r
+ };\r
+\r
_e = {\r
'type' : X_EVENT_BACKEND_READY,\r
- 'source' : e[ 'source' ],\r
- 'backendName' : e[ 'backendName' ]\r
+ 'source' : src,\r
+ 'backendName' : name\r
};\r
\r
if( X_Audio_Sprite_needTouchFirst ){\r
- if( e.backendName === 'Web Audio' ){\r
+ if( name === 'Web Audio' ){\r
_e[ 'needTouchForPlay' ] = true;\r
} else {\r
_e[ 'needTouchForLoad' ] = true;\r
};\r
this[ 'asyncDispatch' ]( _e );\r
\r
- e.target\r
- [ 'unlisten' ]( X_EVENT_BACKEND_NONE, this, X_Audio_Sprite_handleEvent )\r
- [ 'listenOnce' ]( X_EVENT_READY, this, X_Audio_Sprite_handleEvent );\r
+ last[ 'listenOnce' ]( X_EVENT_READY, this, X_AudioSprite_backendHandler );\r
\r
// READY, needTouchForPlay, needTouchForLoad\r
if( X_Audio_HTMLAudioWrapper_durationFix ){\r
for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){\r
- X_Audio_Sprite_instance.pause( i );\r
+ this[ 'pause' ]( i );\r
};\r
};\r
+ \r
+ return X_Callback_STOP_NOW;\r
break;\r
\r
case X_EVENT_BACKEND_NONE :\r
- this[ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
- e.target[ 'unlisten' ]( X_EVENT_BACKEND_READY, this, X_Audio_Sprite_handleEvent );\r
+ this[ 'unlisten' ]( X_EVENT_BACKEND_READY, this, X_AudioSprite_backendHandler )\r
+ [ 'asyncDispatch' ]( X_EVENT_BACKEND_NONE );\r
+ return X_Callback_STOP_NOW;\r
break;\r
\r
case X_EVENT_READY :\r
console.log( 'X.AudioSprite - Ready!' );\r
+ \r
if( X_Audio_Sprite_needTouchAndroid ){\r
for( i = 0; i < X_Audio_Sprite_TEMP.tracks.length; ++i ){\r
- X_Audio_Sprite_instance.pause( i );\r
+ this[ 'pause' ]( i );\r
};\r
e.target[ 'listenOnce' ]( X_EVENT_MEDIA_PLAYING, this, this.asyncDispatch, [ X_EVENT_READY ] ); // Android 標準ブラウザ\r
return;\r
};\r
this[ 'asyncDispatch' ]( X_EVENT_READY );\r
break;\r
- \r
+ };\r
+};\r
+\r
+\r
+function X_Audio_Sprite_handleEvent( e ){\r
+ var i, tracks, track, _e, k;\r
+ \r
+ switch( e.type ){\r
case X_EVENT_MEDIA_PLAYING :\r
- ( e.target === X_Audio_Sprite_TEMP.bgmTrack || !e.target.state().looped ) && this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING );\r
+ ( e.target === X_Audio_Sprite_TEMP.bgmTrack || !e.target.looped ) && this[ 'asyncDispatch' ]( X_EVENT_MEDIA_PLAYING );\r
break;\r
\r
case X_EVENT_MEDIA_BEFORE_LOOP :\r
X_Audio_Sprite_TEMP.bgmLooped = true;\r
this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid\r
} else {\r
- if( e.target.state().looped ){\r
+ if( e.target.looped ){\r
//this[ 'asyncDispatch' ]( X_EVENT_MEDIA_LOOPED ); // TODO uid\r
} else {\r
this[ 'asyncDispatch' ]( X_EVENT_MEDIA_ENDED ); // TODO uid\r
};\r
\r
+ console.log( '[AudioSprite] ' + X_Audio_Sprite_TEMP.bgmPlaying + ' ' + !X_Audio_Sprite_TEMP.bgmTrack );\r
+ \r
// single track | iOS\r
if( X_Audio_Sprite_TEMP.bgmPlaying && !X_Audio_Sprite_TEMP.bgmTrack ){\r
X_Audio_Sprite_TEMP.bgmTrack = e.target;\r
};\r
break;\r
\r
+ // TODO Android Firefox で アクティブ検出できない!\r
case X_EVENT_VIEW_ACTIVATE :\r
console.log( '■ アクティブ' );\r
// track.play(); or iOS need touch??\r
tracks = X_Audio_Sprite_TEMP.pauseTracks;\r
- while( tracks.length ) tracks.pop().play();\r
+ while( tracks.length ) tracks.pop().actualPlay();\r
break;\r
\r
case X_EVENT_VIEW_DEACTIVATE :\r
i = tracks.length;\r
for( ; i; ){\r
track = tracks[ --i ];\r
- track.isPlaying() && X_Audio_Sprite_TEMP.pauseTracks.push( track.pause() );\r
+ track.playing && X_Audio_Sprite_TEMP.pauseTracks.push( track ) && track.pause();\r
};\r
break;\r
\r
case X_EVENT_KILL_INSTANCE :\r
+ \r
+ while( X_Audio_Sprite_TEMP.tracks.length ){\r
+ X_Audio_Sprite_TEMP.tracks.pop()[ 'kill' ]();\r
+ };\r
+ \r
+ for( k in X_Audio_Sprite_TEMP.bgms ){\r
+ delete X_Audio_Sprite_TEMP.bgms[ k ];\r
+ };\r
+ for( k in X_Audio_Sprite_TEMP.presets ){\r
+ delete X_Audio_Sprite_TEMP.presets[ k ];\r
+ };\r
+ \r
+ X_Audio_Sprite_TEMP.bgmTrack = null;\r
+ X_Audio_Sprite_TEMP.bgmPosition = 0;\r
+ X_Audio_Sprite_TEMP.bgmName = '';\r
+ X_Audio_Sprite_TEMP.bgmLooped = false;\r
+ X_Audio_Sprite_TEMP.bgmPlaying = false;\r
+ \r
X_ViewPort[ 'unlisten' ]( [ X_EVENT_VIEW_ACTIVATE, X_EVENT_VIEW_DEACTIVATE ], this, X_Audio_Sprite_handleEvent );\r
- this.close();\r
break;\r
};\r
};\r
\r
currentRootData : null\r
};\r
-\r
-/*\r
- * 'none,chrome,container' を受け取ったら、\r
- * {\r
- * 'none' : 1,\r
- * 'chrome' : 2,\r
- * 'container' : 3,\r
- * 1 : 'none',\r
- * 2 : 'chrome',\r
- * 3 : 'container'\r
- * } こんな object を返す。\r
- */\r
-function XUI_createChecker( str ){\r
- var ret = {},\r
- ary = str.split( ',' ),\r
- l = ary.length,\r
- i = 0, v;\r
- for( ; i < l; ){\r
- v = ary[ i ];\r
- ret[ v ] = ++i;\r
- ret[ i ] = v;\r
- };\r
- return ret;\r
-};\r
\r
XUI_AttrClass = X_Class_create( 'XUI_AttrClass', X_Class.POOL_OBJECT );\r
\r
+/*\r
+ * 'none,chrome,container' を受け取ったら、\r
+ * {\r
+ * 'none' : 1,\r
+ * 'chrome' : 2,\r
+ * 'container' : 3,\r
+ * 1 : 'none',\r
+ * 2 : 'chrome',\r
+ * 3 : 'container'\r
+ * } こんな object を返す。\r
+ */\r
+function XUI_createChecker( str ){\r
+ var ret = {},\r
+ ary = str.split( ',' ),\r
+ l = ary.length,\r
+ i = 0, v;\r
+ for( ; i < l; ){\r
+ v = ary[ i ];\r
+ ret[ v ] = ++i;\r
+ ret[ i ] = v;\r
+ };\r
+ return ret;\r
+};\r
\r
function XUI_Attr_createAttrDef( base, defs ){\r
var F = base ? X_Object_clone( base ) : {},\r
var XUI_AbstractUINode = X_EventDispatcher[ 'inherits' ](\r
'X.UI._AbstractUINode',\r
- X_Class.ABSTRACT | X_Class.PRIVATE_DATA,\r
+ X_Class.ABSTRACT,\r
{\r
phase : 0,\r
dirty : XUI_Dirty.CLEAN,\r
X_Class.ABSTRACT,\r
{\r
parent : function(){\r
- return X_Class_getPrivate( this ).parent;\r
+ return X_Pair_get( this ).parent;\r
},\r
root : function(){\r
- return X_Class_getPrivate( this ).root;\r
+ return X_Pair_get( this ).root;\r
},\r
\r
/*\r
* サポートされていない場合は無視される.親のレイアウトによって変わる\r
*/\r
attr : function( nameOrObject, valueOrUnit ){\r
- var p = X_Class_getPrivate( this ),\r
+ var p = X_Pair_get( this ),\r
layout, k, def, attrs, v;\r
if( nameOrObject && X_Type_isObject( nameOrObject ) ){\r
// setter\r
},\r
\r
listen : function( type, arg1, arg2, arg3 ){\r
- X_Class_getPrivate( this )[ 'listen' ]( type, arg1, arg2, arg3 );\r
+ X_Pair_get( this )[ 'listen' ]( type, arg1, arg2, arg3 );\r
return this;\r
},\r
listenOnce : function( type, arg1, arg2, arg3 ){\r
- X_Class_getPrivate( this )[ 'listenOnce' ]( type, arg1, arg2, arg3 );\r
+ X_Pair_get( this )[ 'listenOnce' ]( type, arg1, arg2, arg3 );\r
return this;\r
},\r
listening : function( type, arg1, arg2, arg3 ){\r
- return X_Class_getPrivate( this )[ 'listening' ]( type, arg1, arg2, arg3 );\r
+ return X_Pair_get( this )[ 'listening' ]( type, arg1, arg2, arg3 );\r
},\r
unlisten : function( type, arg1, arg2, arg3 ){\r
- X_Class_getPrivate( this )[ 'unlisten' ]( type, arg1, arg2, arg3 );\r
+ X_Pair_get( this )[ 'unlisten' ]( type, arg1, arg2, arg3 );\r
return this;\r
},\r
dispatch : function( e ){\r
- return X_Class_getPrivate( this )[ 'dispatch' ]( e );\r
+ return X_Pair_get( this )[ 'dispatch' ]( e );\r
},\r
\r
nextNode : function(){\r
\r
},\r
nodeIndex : function( v ){\r
- var data = X_Class_getPrivate( this );\r
+ var data = X_Pair_get( this );\r
if( typeof v === 'number' ){\r
// data.nodeIndex( v );\r
return this;\r
},\r
getX : function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).boxX;\r
+ return X_Pair_get( this ).boxX;\r
},\r
getY : function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).boxY;\r
+ return X_Pair_get( this ).boxY;\r
},\r
getAbsoluteX : function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).absoluteX;\r
+ return X_Pair_get( this ).absoluteX;\r
},\r
getAbsoluteY: function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).absoluteY;\r
+ return X_Pair_get( this ).absoluteY;\r
},\r
getWidth : function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).boxWidth;\r
+ return X_Pair_get( this ).boxWidth;\r
},\r
getHeight : function(){\r
// dirty の場合、rootData.calculate\r
- return X_Class_getPrivate( this ).boxHeight;\r
+ return X_Pair_get( this ).boxHeight;\r
}\r
}\r
);\r
\r
var XUI_Box = XUI_AbstractUINode.inherits(\r
'X.UI._Box',\r
- X_Class.PRIVATE_DATA, // 現状 super 指定がないとconstructor未定擬時に親のconstructor が使われない\r
+ X_Class.NONE,\r
{\r
supportAttrs : XUI_Attr_createAttrDef( XUI_AbstractUINode.prototype.supportAttrs, XUI_Layout_Canvas.overrideAttrsForSelf ),\r
\r
scrollingX : 0, // TODO 現在のスクロール位置\r
scrollingY : 0, // TODO \r
\r
- Constructor : function( layout, args ){\r
+ Constructor : function( user, layout, args ){\r
var i = 0,\r
l = args.length || 1,\r
j = -1,\r
\r
//if( !args.length ) args = [ args ];\r
\r
- if( !this.User[ 'instanceOf' ]( X.UI.Box ) ){\r
+ if( !user[ 'instanceOf' ]( X.UI.Box ) ){\r
//throw new Error( 'Box を継承したインスタンスだけが _Box のオーナーになれます' );\r
}; \r
\r
+ this.User = user;\r
+ \r
this.xnode = X_Doc_create( 'div' );\r
\r
// すでに定義されていればそちらを採用\r
for( ; i < l; ++i ){\r
arg = args[ i ];\r
if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.AbstractUINode ) ){\r
- _data = X_Class_getPrivate( arg );\r
+ _data = X_Pair_get( arg );\r
if( !uinodes ) this.uinodes = uinodes = [];\r
uinodes[ ++j ] = _data;\r
if( _data.parent ){\r
\r
//console.log( '### AddAt ' + this.phase )\r
for( l = _uinodes.length; i < l; ++i ){\r
- data = X_Class_getPrivate( _uinodes[ i ] );\r
+ data = X_Pair_get( _uinodes[ i ] );\r
_p1 = p1 && data.phase < 1;\r
_p2 = p2 && data.phase < 2;\r
_p1 && data.initialize( this.root, this.rootData, this.User, this );\r
\r
//console.log( '### AddAt ' + this.phase )\r
for( ; i; ){\r
- data = X_Class_getPrivate( _uinodes[ --i ] );\r
+ data = X_Pair_get( _uinodes[ --i ] );\r
if( ( n = uinodes.indexOf( data ) ) !== -1 ){\r
uinodes.splice( n, 1 );\r
data._remove();\r
X.UI.Box = X.UI.AbstractUINode.inherits(\r
'Box',\r
X_Class.NONE,\r
- XUI_Box,\r
{\r
Constructor : function(){\r
- X_Class_newPrivate( this, XUI_Layout_Canvas, arguments );\r
+ X_Pair_create( this, XUI_Box( this, XUI_Layout_Canvas, arguments ) );\r
},\r
\r
add : function( node /* , node, node ... */ ){\r
- X_Class_getPrivate( this ).addAt( this.numNodes() + 1, Array.prototype.slice.call( arguments ) );\r
+ X_Pair_get( this ).addAt( this.numNodes() + 1, Array.prototype.slice.call( arguments ) );\r
return this;\r
},\r
addAt : function( index, node /* , node, node ... */ ){\r
if( index < 0 ) index = 0;\r
- X_Class_getPrivate( this ).addAt( arguments[ 0 ], Array.prototype.slice.call( arguments, 1 ) );\r
+ X_Pair_get( this ).addAt( arguments[ 0 ], Array.prototype.slice.call( arguments, 1 ) );\r
return this;\r
},\r
remove : function( node /* , node, node ... */ ){\r
- X_Class_getPrivate( this )[ 'remove' ]( Array.prototype.slice.call( arguments ) );\r
+ X_Pair_get( this )[ 'remove' ]( Array.prototype.slice.call( arguments ) );\r
return this;\r
},\r
removeAt : function( from, length ){\r
- X_Class_getPrivate( this ).removeAt( from, length );\r
+ X_Pair_get( this ).removeAt( from, length );\r
return this;\r
},\r
getNodesByClass : function( klass ){\r
var ret = [],\r
- uinodes = X_Class_getPrivate( this ).uinodes,\r
+ uinodes = X_Pair_get( this ).uinodes,\r
i, l, node;\r
if( !uinodes || uinodes.length === 0 ) return ret;\r
for( i = 0, l = uinodes.length; i < l; ++i ){\r
return this.getNodeAt( 0 );\r
},\r
getLastChild : function(){\r
- var uinodes = X_Class_getPrivate( this ).uinodes;\r
+ var uinodes = X_Pair_get( this ).uinodes;\r
return uinodes && uinodes.length && uinodes[ uinodes.length - 1 ].User || null;\r
},\r
getNodeAt : function( index ){\r
if( index < 0 ) return null;\r
- var uinodes = X_Class_getPrivate( this ).uinodes;\r
+ var uinodes = X_Pair_get( this ).uinodes;\r
return uinodes && uinodes[ index ].User || null;\r
},\r
numNodes : function(){\r
- var uinodes = X_Class_getPrivate( this ).uinodes;\r
+ var uinodes = X_Pair_get( this ).uinodes;\r
return uinodes && uinodes.length || 0;\r
}\r
}\r
};\r
\r
if( privateKlass ){\r
+ /*\r
+ * スーパークラスの属性定義リストをレイアウトの持つ属性定義で上書きした新しい属性定義リストを作る。\r
+ */\r
supports = XUI_Attr_createAttrDef( privateKlass.prototype.supportAttrs, layout.overrideAttrsForSelf );\r
\r
klass = this.inherits( privateKlass );\r
- privateKlass.prototype.supportAttrs = supports,\r
+ privateKlass.prototype.supportAttrs = supports;\r
privateKlass.prototype.attrClass = XUI_Attr_preset( privateKlass.prototype.attrClass, supports, attrs );\r
} else {\r
supports = XUI_Attr_createAttrDef( shadow.prototype.supportAttrs, layout.overrideAttrsForSelf );\r
\r
name : 'VerticalLayout',\r
\r
+ /*\r
+ * \r
+ */\r
overrideAttrsForSelf : {\r
selectable : false,\r
- role : [ 0, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.INIT_ONLY | XUI_Attr_Type.LIST, 'none,chrome,container' ],\r
- width : [ '100%', XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
+ role : [ 0, XUI_Dirty.CLEAN, XUI_Attr_USER.UINODE, XUI_Attr_Type.INIT_ONLY | XUI_Attr_Type.LIST, 'none,chrome,container' ],\r
+ width : [ '100%', XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
height : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
childWidth : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
childHeight : [ XUI_Attr_AUTO, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH | XUI_Attr_Type.PERCENT | XUI_Attr_Type.AUTO ],\r
- gapY : [ 0, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH ]\r
+ gapY : [ 0, XUI_Dirty.LAYOUT, XUI_Attr_USER.LAYOUT, XUI_Attr_Type.LENGTH ]\r
},\r
\r
overrideAttrsForChild : {\r
} else {\r
w = 0;\r
};\r
- if( _w < w ) _w = w; \r
- }; \r
+ if( _w < w ) _w = w;\r
+ };\r
};\r
_y -= gapY;\r
} else {\r
\r
if( !isNeedsDetection ){\r
data.boxX += x;\r
- data.boxY += y; \r
+ data.boxY += y;\r
};\r
return !ret;\r
}\r
});\r
\r
-X.UI.VBox = X.UI.Box.presets(\r
- XUI_Layout_Vertical,\r
+var XUI_VBox;\r
+\r
+X.UI.VBox = X.UI.Box.inherits(\r
'VBox',\r
+ X_Class.NONE,\r
{\r
- gapY : '0.2em',\r
- childWidth : '100%'\r
- }\r
-);\r
+ Constructor : function(){\r
+ var supports;\r
+ \r
+ if( !XUI_VBox ){\r
+ supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Vertical.overrideAttrsForSelf );\r
+ \r
+ XUI_VBox = XUI_Box.inherits(\r
+ {\r
+ layout : XUI_Layout_Vertical,\r
+ supportAttrs : supports,\r
+ attrClass : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+ gapY : '0.2em',\r
+ childWidth : '100%'\r
+ } )\r
+ }\r
+ );\r
+ };\r
+ X_Pair_create( this, XUI_VBox( this, XUI_Layout_Vertical, arguments ) );\r
+ }\r
+ });\r
+\r
}\r
});\r
\r
+/*\r
X.UI.HBox = X.UI.Box.presets(\r
XUI_Layout_Horizontal,\r
'HBox',\r
{\r
gapX : '0.2em'\r
}\r
-);\r
+);*/\r
+\r
+var XUI_HBox;\r
+\r
+X.UI.HBox = X.UI.Box.inherits(\r
+ 'HBox',\r
+ X_Class.NONE,\r
+ {\r
+ Constructor : function(){\r
+ var supports;\r
+ \r
+ if( !XUI_HBox ){\r
+ supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Horizontal.overrideAttrsForSelf );\r
+ \r
+ XUI_HBox = XUI_Box.inherits(\r
+ {\r
+ layout : XUI_Layout_Horizontal,\r
+ supportAttrs : supports,\r
+ attrClass : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+ gapX : '0.2em'\r
+ } )\r
+ }\r
+ ); \r
+ };\r
+ X_Pair_create( this, XUI_HBox( this, XUI_Layout_Horizontal, arguments ) );\r
+ }\r
+ });
\ No newline at end of file
}\r
});\r
\r
+/*\r
X.UI.TileBox = X.UI.Box.presets(\r
'TileBox',\r
XUI_Layout_Tile,\r
hCenter : true,\r
vCenter : true\r
}\r
-);\r
+);*/\r
+\r
+X.UI.TileBox = X.UI.Box.inherits(\r
+ 'TileBox',\r
+ X_Class.NONE,\r
+ {\r
+ Constructor : function(){\r
+ var supports;\r
+ \r
+ if( !XUI_TileBox ){\r
+ supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Tile.overrideAttrsForSelf );\r
+ \r
+ XUI_TileBox = XUI_Box.inherits(\r
+ {\r
+ layout : XUI_Layout_Tile,\r
+ supportAttrs : supports,\r
+ attrClass : XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {\r
+ gapX : '0.2em',\r
+ gapY : '0.2em',\r
+ hCenter : true,\r
+ vCenter : true\r
+ })\r
+ }\r
+ ); \r
+ };\r
+ X_Pair_create( this, XUI_TileBox( this, XUI_Layout_Tile, arguments ) );\r
+ }\r
+ });
\ No newline at end of file
var XUI_ChromeBox = XUI_Box.inherits(\r
'_ChromeBox',\r
- X_Class.PRIVATE_DATA,\r
+ X_Class.NONE,\r
{\r
chromeNodes : null,\r
containerNode : null,\r
\r
- Constructor : function( layout, args ){\r
+ Constructor : function( user, layout, args ){\r
var uinodes, i, l, node, after, index = 0;\r
\r
- this[ 'Super' ]( layout, args );\r
+ this[ 'Super' ]( user, layout, args );\r
\r
uinodes = this.uinodes;\r
l = i = uinodes.length;\r
if( this.containerNode ){\r
//throw new Error( 'ContainerNode が複数設定されています!ContainerNode はクロームボックスにひとつ、生成時に設定できます ' + node );\r
};\r
- this.containerNode = node.User;\r
+ this.containerNode = node.User;\r
this._containerNode = node;\r
} else {\r
if( !this.chromeNodes ) this.chromeNodes = [];\r
X.UI.ChromeBox = X.UI.Box.inherits(\r
'ChromeBox',\r
X_Class.NONE,\r
- XUI_ChromeBox,\r
{\r
Constructor : function(){\r
- X_Class_newPrivate( this, XUI_Layout_Canvas, arguments );\r
+ X_Pair_create( this, XUI_ChromeBox( this, XUI_Layout_Canvas, arguments ) );\r
},\r
add : function( node /* , node, node ... */ ){\r
- X_Class_getPrivate( this ).containerNode.addAt( this.numNodes(), Array.prototype.slice.call( arguments ) );\r
+ X_Pair_get( this ).containerNode.addAt( this.numNodes(), Array.prototype.slice.call( arguments ) );\r
return this;\r
},\r
addAt : function( index, node /* , node, node ... */ ){\r
- X_Class_getPrivate( this ).containerNode.addAt( index, Array.prototype.slice.call( arguments, 1 ) );\r
+ X_Pair_get( this ).containerNode.addAt( index, Array.prototype.slice.call( arguments, 1 ) );\r
return this;\r
},\r
remove : function( node /* , node, node ... */ ){\r
- X_Class_getPrivate( this ).containerNode[ 'remove' ]( arguments );\r
+ X_Pair_get( this ).containerNode[ 'remove' ]( arguments );\r
return this;\r
},\r
removeAt : function( from, length ){\r
- X_Class_getPrivate( this ).containerNode.removeAt( from, length );\r
+ X_Pair_get( this ).containerNode.removeAt( from, length );\r
return this;\r
},\r
getNodesByClass : function( klass ){\r
- return X_Class_getPrivate( this ).containerNode.User.getNodesByClass( klass );\r
+ return X_Pair_get( this ).containerNode.User.getNodesByClass( klass );\r
},\r
getFirstChild : function(){\r
- return X_Class_getPrivate( this ).containerNode.User.getFirstChild();\r
+ return X_Pair_get( this ).containerNode.User.getFirstChild();\r
},\r
getLastChild : function(){\r
- return X_Class_getPrivate( this ).containerNode.User.getLastChild();\r
+ return X_Pair_get( this ).containerNode.User.getLastChild();\r
},\r
getNodeByUID : function( uid ){\r
- return X_Class_getPrivate( this ).containerNode.User.getNodeByUID();\r
+ return X_Pair_get( this ).containerNode.User.getNodeByUID();\r
},\r
getNodeAt : function( index ){\r
- return X_Class_getPrivate( this ).containerNode.User.getNodeAt( index );\r
+ return X_Pair_get( this ).containerNode.User.getNodeAt( index );\r
},\r
numNodes : function(){\r
- return X_Class_getPrivate( this ).containerNode.User.numNodes();\r
+ return X_Pair_get( this ).containerNode.User.numNodes();\r
},\r
getContainerNode : function(){\r
- return X_Class_getPrivate( this ).containerNode.User;\r
+ return X_Pair_get( this ).containerNode.User;\r
},\r
getChromeNodeAt : function( index ){\r
if( index < 0 ) return null;\r
- var nodes = X_Class_getPrivate( this ).chromeNodes;\r
+ var nodes = X_Pair_get( this ).chromeNodes;\r
return nodes ? nodes[ index ].User || null : null;\r
},\r
numChromeNodes : function(){\r
- var nodes = X_Class_getPrivate( this ).chromeNodes;\r
+ var nodes = X_Pair_get( this ).chromeNodes;\r
return nodes ? nodes.length : 0;\r
}\r
}\r
\r
var XUI_ScrollBox = XUI_ChromeBox.inherits(\r
'_ScrollBox',\r
- X_Class.PRIVATE_DATA,\r
+ X_Class.NONE,\r
{\r
directionLockThreshold : 10,\r
scrollXEnabled : true,\r
_containerNode : null,\r
xnodeSlider : null,\r
\r
- Constructor : function( layout, args ){\r
- this[ 'Super' ]( layout, args );\r
- this._containerNode = X_Class_getPrivate( this.containerNode );\r
+ Constructor : function( user, layout, args ){\r
+ this[ 'Super' ]( user, layout, args );\r
+ this._containerNode = X_Pair_get( this.containerNode );\r
this.xnodeSlider = this._containerNode.xnode[ 'className' ]( 'ScrollSlider' ).listen( X_EVENT_ANIME_END, this, X_UI_ScrollBox_onAnimeEnd );\r
this.xnode[ 'className' ]( 'ScrollBox' );\r
},\r
};\r
if( !X_UI_ScrollBox_resetPosition( this, this.bounceTime ) ){\r
this.isInTransition = false;\r
- this.dispatch( XUI_Event.SCROLL_END );\r
+ this[ 'dispatch' ]( XUI_Event.SCROLL_END );\r
};\r
return X_Callback_NONE;\r
};\r
\r
+// TODO Box の継承に!\r
X.UI.ScrollBox = X.UI.ChromeBox.inherits(\r
'ScrollBox',\r
X_Class.NONE,\r
- XUI_ScrollBox,\r
{\r
Constructor : function(){\r
var args = [\r
- XUI_Layout_Vertical, \r
+ XUI_Layout_Canvas, \r
{\r
- name : 'ScrollBox-Scroller',\r
- role : 'container',\r
- width : 'auto',\r
- minWidth : '100%',\r
- height : 'auto',\r
- minHeight : '100%'\r
+ name : 'ScrollBox-Scroller',\r
+ role : 'container',\r
+ width : 'auto',\r
+ minWidth : '100%',\r
+ height : 'auto',\r
+ minHeight : '100%'\r
}\r
],\r
i = arguments.length,\r
};\r
};\r
\r
- X_Class_newPrivate(\r
+ X_Pair_create(\r
this,\r
- XUI_Layout_Canvas,\r
- [\r
- {\r
- width : '100%',\r
- height : '100%'\r
- },\r
- X.UI.VBox.apply( 0, args )\r
- ]\r
+ XUI_ScrollBox(\r
+ this,\r
+ XUI_Layout_Canvas,\r
+ [\r
+ {\r
+ width : '100%',\r
+ height : '100%'\r
+ },\r
+ X.UI.VBox.apply( 0, args )\r
+ ]\r
+ )\r
);\r
\r
attr && this.attr( attr );\r
var XUI_Text = XUI_AbstractUINode.inherits(\r
'_Text',\r
- X_Class.PRIVATE_DATA,\r
+ X_Class.NONE,\r
{\r
content : null,\r
\r
- Constructor : function( content ){\r
- if( !( this.User[ 'instanceOf' ]( X.UI.Text ) ) ){\r
+ Constructor : function( user, content ){\r
+ if( !( user[ 'instanceOf' ]( X.UI.Text ) ) ){\r
alert( 'Text を継承したインスタンスだけが _Text のオーナーになれます' );\r
};\r
- this.xnode = X_Doc_create( 'div' );\r
+ this.User = user;\r
+ this.xnode = X_Doc_create( 'div' );\r
\r
if( X_Type_isString( content ) && content ){\r
this.content = content;\r
X.UI.Text = X.UI.AbstractUINode.inherits(\r
'Text',\r
X_Class.NONE,\r
- XUI_Text,\r
{\r
Constructor : function( opt_content, opt_attrObj ){\r
- X_Class_newPrivate( this, opt_content );\r
+ X_Pair_create( this, XUI_Text( this, opt_content ) );\r
+\r
X_Type_isObject( opt_attrObj = opt_attrObj || opt_content ) && this[ 'attr' ]( opt_attrObj );\r
},\r
content : function( v ){\r
- var data = X_Class_getPrivate( this );\r
+ var data = X_Pair_get( this );\r
if( data.content !== v ){\r
data.xnode && data.xnode[ 'text' ]( v );\r
data.rootData.reserveCalc();\r
function X_UI_eventRellay( e ){
var font = X_ViewPort_baseFontSize,
- x = e.pageX / font, // clientX iOS4- で通らない?
+ x = e.pageX / font, // clientX は iOS4- で通らない?
y = e.pageY / font,
type = XUI_Event.NameToID[ e.type ],
i = 0,
var XUI_PageRoot = XUI_Box.inherits(
'_PageRoot',
- X_Class.FINAL | X_Class.PRIVATE_DATA,
+ X_Class.FINAL,
{
layout : XUI_Layout_Canvas,
eventCounter : null,
cursorStyle : null,
- Constructor : function( layout, args ){
- this[ 'Super' ]( layout, args );
+ Constructor : function( user, layout, args ){
+ this[ 'Super' ]( user, layout, args );
if( X_ViewPort_readyState === X_EVENT_XDOM_READY ){
X_Timer_once( 0, this, this.start );
this[ 'dispatch' ]( XUI_Event.LAYOUT_COMPLETE );
};
+//var XUI_PageRoot;
+
+X.UI.PageRoot = X.UI.Box.inherits(
+ 'PageRoot',
+ X_Class.NONE,
+ {
+ Constructor : function(){
+ var supports;
+
+ //if( !XUI_PageRoot ){
+ supports = XUI_Attr_createAttrDef( XUI_Box.prototype.supportAttrs, XUI_Layout_Canvas.overrideAttrsForSelf );
+
+ XUI_PageRoot.prototype.layout = XUI_Layout_Canvas;
+ XUI_PageRoot.prototype.supportAttrs = supports;
+ XUI_PageRoot.prototype.attrClass = XUI_Attr_preset( XUI_Box.prototype.attrClass, supports, {
+ width : '100%',
+ height : '100%'
+ } );
+ //};
+ X_Pair_create( this, XUI_PageRoot( this, XUI_Layout_Canvas, arguments ) );
+ }
+ });
+/*
X.UI.PageRoot = X.UI.Box.presets(
'PageRoot',
XUI_PageRoot,
width : '100%',
height : '100%'
}
-);
+);*/