
var
	ease = {
		'quadratic' : {
			style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
			fn: function (k) {
				return k * ( 2 - k );
			}
		},
		'circular' : {
			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)
			fn: function (k) {
				return Math.sqrt( 1 - ( --k * k ) );
			}
		},
		'back' : {
			style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
			fn: function (k) {
				var b = 4;
				return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
			}
		},
		'bounce' : {
			style: '',
			fn: function (k) {
				if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
					return 7.5625 * k * k;
				} else if ( k < ( 2 / 2.75 ) ) {
					return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
				} else if ( k < ( 2.5 / 2.75 ) ) {
					return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
				} else {
					return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
				}
			}
		},
		'elastic' : {
			style: '',
			fn: function (k) {
				var f = 0.22,
					e = 0.4;

				if ( k === 0 ) { return 0; }
				if ( k == 1 ) { return 1; }

				return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
			}
		}
	};

// お約束
// transform や transition animation は スタイルシートに書かない

// 新規アニメーションが追加された場合、
// tree が dirty なら AFTER_COMMIT を待つ
// 1) xnode の既存アニメーションとの親子関係の調査
// 親なら -> 既存アニメーションの GPU レイヤー解除
// 子なら -> GPU レイヤーを設定しない
// 2) 目標座標のセット
// 3) アニメーション完了後も GPU レイヤーはしばらく解除しない スクロール等の連続アニメーション時に GPU 転送時間で画面ががたつくから
// アニメ中の remove

var X_Node_ANIMATIONS            = [],
	X_Node_Anime_reserved        = false,
	X_Node_Anime_updateTimerID   = 0,
	X_Node_Anime_needsDetection  = false,
	X_Node_Anime_onTransition    = false,
	
	X_Node_Anime_hasTransform    = !!X_Node_CSS_VENDER_PREFIX[ 'transform' ],
	/* Opera mobile で  translateZ(0) が有効だと XY が 0 0 になる */
	/* GPUレイヤーにいる間に要素のコンテンツを変更をすると transitionend が動かなくなるっぽい Mac safari と firefox */
	X_Node_Anime_translateZ      = X_Node_CSS_VENDER_PREFIX[ 'perspective' ] && !X_UA[ 'OperaMobile' ] && !X_UA[ 'OperaTablet' ] ? ' translateZ(0)' : '',
	/* Opera12(XP,8.1) 切った方がスムース, win Safari3 で、たまに動作が止まってしまう、、、 */
	X_Node_Anime_hasTransition   = false && !!X_Node_CSS_VENDER_PREFIX[ 'transitionDelay' ] && !( X_UA[ 'iOS' ] < 6 ) && !X_UA[ 'Opera' ] && !X_UA[ 'Blink' ], // && !( X_UA[ 'Webkit' ]  <= 528.16 ),
	X_Node_Anime_transitionProps = X_Node_Anime_hasTransform ? X_Node_CSS_VENDER_PREFIX[ 'transform' ] : 'left,top',
	// transitionEnd イベント中に要素の更新( X_Node_startUpdate() )ができるか？
	// iOS3+4 では可能、iOS6.1.5 で不可。TODO iOS5　及び他の環境で調査。ダメな場合、anime.html が正しく描画されない。
	X_Node_updateOnTransitionEnd = false;

// gpu化だけ transformX , willChange
// 終了位置の変更
// 中断

/*
 * TODO : DX Anime
 * TODO : scale,  ActiveX transform, zoom, fontSizeScale
 * TODO : rotate, ActiveX transform -> 位置補正のために size 情報が必要なので、commitUpdate 後に計算して適用
 * TODO : matrix
 * TODO : skew
 * TODO : filter
 * TODO 前回位置からの継続
 * TODO scrollLeft, scrollTop
 */
/**
 * GPU サポートの効いたアニメーションの設定 X.Event.ANIME_START, X.Event.ANIME_END, X.Event.GPU_RELEASED
 * 
 * @alias Node.prototype.animate
 * @param {object} start { x : 0, y : 0, opacity : 1 }
 * @param {object} dest  { x : 100, y : 100, opacity : 0 }
 * @param {number=} duartion アニメーション時間 ms
 * @param {string=} easing 'quadratic', 'circular', 'back', 'bounce', 'elastic'
 * @param {number=} wait GPU レイヤーの遅延解除 ms
 * @return {Node} メソッドチェーン
 */
function X_Node_animate( start, dest, duration, easing, wait ){
	var isNew = !this[ '_anime' ],
		obj   = this[ '_anime' ];
	
	if( !obj ){
		this[ '_anime' ] = obj = X_Node_Anime_getComputedPosition( this );
		obj.destX = obj.x;
		obj.destY = obj.y;
		obj.destA = obj.a;
	};
	
	obj.duration  = 0 <= duration && X_Type_isFinite( duration ) ? duration : 500;
	obj.easing    = ease[ easing ] || ease[ 'circular' ];
	// 現在 GPUレイヤーのtop になっているか？将来については phase で判定
	obj.gpuParent = obj.gpuParent || false;
	obj.phase     = duration === 0 ? 9 : obj.phase === 9 ? 9 : 0; //
	obj.wait      = X_Type_isFinite( wait ) ? wait : 1000;
		
	obj.startTime = X_Timer_now();
	obj.startX    = ( start.x || start.x === 0 ) ? start.x : obj.x || NaN;
	obj.startY    = ( start.y || start.y === 0 ) ? start.y : obj.y || NaN;
	obj.startA    = 0 <= start.opacity && start.opacity <= 1 ? start.opacity : obj.a || 1;
	
	obj.destTime  = obj.startTime + obj.duration;
	obj.destX     = ( dest.x || dest.x === 0 ) ? dest.x : obj.destX || NaN;
	obj.destY     = ( dest.y || dest.y === 0 ) ? dest.y : obj.destY || NaN;
	obj.destA     = 0 <= dest.opacity && dest.opacity <= 1 ? dest.opacity : obj.destA || 1;

	X_Node_ANIMATIONS.indexOf( this ) === -1 &&
		( X_Node_ANIMATIONS[ X_Node_ANIMATIONS.length ] = this );

	
	if( X_Node_Anime_onTransition ) return this;

	if( X_Node_Anime_hasTransition ){
		
		if( obj.gpuTimerID ){
			X_Timer_remove( obj.gpuTimerID );
			delete obj.gpuTimerID;
		};		
		
		X_Node_Anime_needsDetection = true;
		
		X_Node_Anime_reserveUpdate( true );
	} else {
		if( !X_Node_Anime_reserved ){
			X_Node_Anime_reserved = true;
			if( X_Node_updateTimerID ){
				X_System[ 'listen' ]( X_EVENT_UPDATED, X_Node_Anime_updateAnimationsNoTransition );
				X_Node_Anime_updateTimerID && X_Timer_cancelFrame( X_Node_Anime_updateTimerID );
				X_Node_Anime_updateTimerID = 0;
			} else {
				X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_Node_Anime_updateAnimationsNoTransition );
				X_Node_Anime_updateTimerID = X_Timer_requestFrame( X_Node_Anime_updateAnimationsNoTransition );
			};
		};
		
		isNew && this[ 'dispatch' ]( { type : X_EVENT_ANIME_START, 'gpu' : false } );
	};
	
	// console.log( 'animate() ' + this[ '_id' ] + ' y:' + obj.startY + ' > ' + obj.destY + ' d:' + obj.duration );
	
	return this;
};

/**
 * アニメーションの停止。
 * @alias Node.prototype.stop
 * @return {Node} メソッドチェーン
 */
function X_Node_stop(){
	var obj = this[ '_anime' ];
	
	if( !obj ) return this;
	
	if( X_Node_Anime_hasTransition ){
		obj.phase = 100;
		X_Node_Anime_needsDetection = true;
		X_Node_Anime_reserveUpdate();
	} else {
		X_Node_ANIMATIONS.splice( X_Node_ANIMATIONS.indexOf( this ), 1 );
		//obj.gpuTimerID && X_Timer_remove( obj.gpuTimerID );
		delete this[ '_anime' ];
	};
	return this;
};

function X_Node_Anime_reserveUpdate( before ){
	if( !X_Node_Anime_reserved ){
		X_Node_Anime_reserved = true;
		
		if( X_Node_updateTimerID ){
			//console.log( before ? '> BEFORE_UPDATE' : '> UPDATED' );
			before = false;
			X_System[ 'listenOnce' ]( before ? X_EVENT_BEFORE_UPDATE : X_EVENT_UPDATED, X_Node_Anime_updateAnimations );
		} else {
			//console.log( '> Timer' );
			// Opera12 requestAnimationFrame では transition が動かない、、、
			X_Node_Anime_updateTimerID =
				X_UA[ 'Opera' ] ?
					X_Timer_once( 0, X_Node_Anime_updateAnimations ) :
					X_Timer_requestFrame( X_Node_Anime_updateAnimations );
		};
	} else {
		// console.log( ' X_Node_Anime_reserved 済、予約なし' );
	};
};

function X_Node_Anime_updateAnimations( v, updateNow ){
	var i = X_Node_ANIMATIONS.length, c = false, ret, xnode;
	
	// console.log( v.type || v );
	
	//console.log( 'updateAnimations len:' + i + ' time:' + v + ' det:' + X_Node_Anime_needsDetection );
	
	if( X_Node_Anime_needsDetection ) X_Node_Anime_detectAnimationLayers();
	
	for( ; i; ){
		xnode = X_Node_ANIMATIONS[ --i ];
		//console.log( 'phase : ' + xnode[ '_id' ] + ' ' + xnode[ '_anime' ].phase + ' ' + xnode[ '_anime' ].duration );
		ret = X_Node_Anime_updateAnimation( xnode );
		if( ret === true ){
			X_Node_ANIMATIONS.splice( i, 1 );
			xnode[ '_anime' ].gpuTimerID && X_Timer_remove( xnode[ '_anime' ].gpuTimerID );
			delete xnode[ '_anime' ];
		} else
		if( ret !== false ){
			c = true;
		};
	};
	
	if( ( X_Node_Anime_updateTimerID || updateNow ) && X_Node_updateTimerID ) X_Node_startUpdate();
	
	X_Node_Anime_updateTimerID = 0;
	X_Node_Anime_reserved = false;
	if( c ){
		X_Node_Anime_reserveUpdate();
	};
};

// TODO X_Timer_requestFrame 経由の BEFORE_UPDATE で更新を行う
function X_Node_Anime_detectAnimationLayers(){
	var i = X_Node_ANIMATIONS.length,
		l = i,
		j, xnode, parent, _xnode, hasGPUChild, remove;

	for( ; i; ){
		xnode = X_Node_ANIMATIONS[ --i ];
		parent = hasGPUChild = false;
		for( j = l; j; ){
			_xnode = X_Node_ANIMATIONS[ --j ];
			
			if( xnode.parent === _xnode.parent ){
				continue;
			} else
			if( _xnode[ 'contains' ]( xnode ) ){
				if( ( _xnode[ '_anime' ].phase === 3 && _xnode[ '_anime' ].gpuParent ) || _xnode[ '_anime' ].phase === 10 ){
					_xnode[ '_anime' ].phase = 15;
				} else
				if( xnode[ '_anime' ].gpuParent ){
					parent = true;
					xnode[ '_anime' ].phase = xnode[ '_anime' ].phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄
				} else
				if( [ 7, 8, 9, 13, 14 ].indexOf( xnode[ '_anime' ].phase ) !== -1 ){// GPU レイヤーの中止
					parent = true;
					xnode[ '_anime' ].phase -= 8;
				};
				break;
			} else
			if( xnode[ 'contains' ]( _xnode ) ){
				if( ( xnode[ '_anime' ].phase === 3 && xnode[ '_anime' ].gpuParent ) || xnode[ '_anime' ].phase === 10 ){
					xnode[ '_anime' ].phase = 15;
				} else
				if( _xnode[ '_anime' ].gpuParent ){
					hasGPUChild = true;
					_xnode[ '_anime' ].phase = _xnode[ '_anime' ].phase === 2 ? 6 : 15;// GPU レイヤーの解除 > アニメーションは継続, すでに終了フェイズなら破棄
				} else
				if( [ 7, 8, 9, 13, 14 ].indexOf( _xnode[ '_anime' ].phase ) !== -1 ){// GPU レイヤーの中止
					hasGPUChild = true;
					_xnode[ '_anime' ].phase -= 8;
				};
				break;
			};
		};
		
		if( !parent && xnode[ '_anime' ].phase !== 15 ){
			if( xnode[ '_anime' ].phase === 0 ){
				// 新規
				xnode[ '_anime' ].phase = hasGPUChild ? 7 : 8;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。
			} else
			if( [ 3, 4, 10, 100 ].indexOf( xnode[ '_anime' ].phase ) === -1 ){
				// 非GPU -> GPU
				xnode[ '_anime' ].phase = hasGPUChild ? 13 : 14;// 非GPU -> GPU 子に GPU アニメをもつなら、タイミングをずらす。
			};
		};
	};
	
	X_Node_Anime_needsDetection = false;
};

function X_Node_Anime_updateAnimation( xnode ){
	var obj   = xnode[ '_anime' ],
		phase = obj.phase,
		current, time;
	switch( phase ){
		case 2 :
			// アニメーション中
			return false;
		
		case -1 :// 子の GPU レイヤー解除待ち
		case  7 :
			++obj.phase;
			break;
		case  0 : // 開始位置+アニメーションの設定 
		case  8 :
			X_ViewPort[ 'unlisten' ]( X_EVENT_AFTER_UPDATE, xnode, X_Node_Anime_gpuReleased );
			
			xnode[ 'css' ]({
				//willChange               : X_Node_Anime_transitionProps + ',opacity,width,height',
				backfaceVisibility       : 'hidden',
				transitionTimingFunction : obj.easing.style,
				transitionDelay          : '0s' // 0.001 にすると transitionend のタイミングが狂う、、、
			});
			
			//console.log( '開始位置 ' + phase );
			X_Node_Anime_updatePosition( xnode, obj.startX, obj.startY, obj.startA, phase === 8 );
			
			xnode[ 'dispatch' ]( { type : X_EVENT_ANIME_START, 'gpu' : phase === 8 } );
			++obj.phase;
			break;
		case  1 :
		case  9 : // 終了位置の設定
			obj.gpuParent = phase === 9;
			if( obj.duration ){
				xnode[ 'listenOnce' ]( 'transitionend', X_Node_Anime_onTransitionEnd );
			
				xnode[ 'css' ]({
					transitionProperty : X_Node_Anime_transitionProps + ',opacity,width,height',
					transitionDuration : obj.duration + 'ms'
				});
				
				//console.log( '修了位置 ' + phase );
				X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, obj.gpuParent );
				obj.phase = 2;
				break;
			};
			//console.log( 'duration = 0 の場合、アニメーションの解除' );
			// duration = 0 の場合、アニメーションの解除
			
		case 3 : // TransitionEnd　-> アニメーションの解除
			obj.phase = obj.gpuParent ? 10 : 4;
			
			//console.log( '#### アニメーションの解除 ' + obj.phase );
			
			// このタイミングで animation 関連の css を削除したところ(X_Node_Anime_clearTransition)、iOS3、4 で再描画忘れが度々起きるように、、、
			if( !obj.gpuParent ) X_Node_Anime_clearTransition( xnode );
			break;

		case 4 :
			// アニメーションは停止・GPU=false -> リストから削除
			obj.gpuParent = false;
			return true;

		case 10 :
			// アニメーションは停止・GPUレイヤーは解除していない(再アニメーションに備えて待機)
			if( !obj.gpuTimerID ){
				//console.log( '#### アニメーションは停止 ' + obj.wait );
				if( obj.wait ){
					obj.gpuTimerID = X_Timer_once( obj.wait, xnode, X_Node_Anime_releaseGPULayer );
				} else {
					X_Node_Anime_releaseGPULayer.call( xnode );
				};
			};
			return false;
		
		case  5 :
		case 13 :
			// 子のGPU解除待ち
			++obj.phase;
			break;
		
		// GPU レイヤーの変更> アニメーションは継続,但し残り時間が短ければ停止
		case  6 :		
		case 14 :
			now  = X_Timer_now();
			time = obj.duration - now + obj.startTime;
			if( time < 16 ){
				X_Node_Anime_clearTransition( xnode );
				X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, phase === 14 );
				obj.phase = phase === 14 ? 10 : 4;
				xnode[ 'dispatch' ]( time, { type : X_EVENT_ANIME_END, 'gpu' : obj.gpuParent } );
			} else {
				current = X_Node_Anime_getComputedPosition( xnode );
				obj.startX    = current.x;
				obj.startY    = current.y;
				obj.startA    = current.a;
				obj.duration  = time;
				obj.startTime = now;
				X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, phase === 14 );
				obj.phase = phase === 14 ? 9 : 1;
			};
			break;
		
		case 15 :
			// GPU有効で停止（待機）している xnode の解除
			//console.log( 'GPU有効で停止（待機）している xnode の解除' + xnode[ '_tag' ] + xnode[ 'getOrder' ]() );
			// console.log( 'GPU有効で停止（待機）している xnode のGPU解除' );
			X_Node_Anime_clearTransition( xnode );
			X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );
			//obj.gpuTimerID && X_Timer_remove( obj.gpuTimerID );
			//delete obj.gpuTimerID;
			X_ViewPort[ 'listenOnce' ]( X_EVENT_AFTER_UPDATE, xnode, X_Node_Anime_gpuReleased );
			return true;
		
		case 100 : // stop() : アニメーションを中断して削除
			//console.log( 'stop() gpu:' + obj.gpuParent );
			// console.log( 'アニメーションを中断して削除' );
			current = X_Node_Anime_getComputedPosition( xnode );
						
			X_Node_Anime_clearTransition( xnode );
			X_Node_Anime_updatePosition( xnode, current.x, current.y, current.a, obj.gpuParent );
			obj.phase = obj.gpuParent ? 10 : 4;
			break;
		
	};
};

function X_Node_Anime_getComputedPosition( that ){
	var raw = that[ '_rawObject' ],
		x = NaN, y = NaN, a = 1, style, matrix;

	if( raw ){
		if( X_Node_Anime_hasTransform ){
			if( style = X_Node_CSS_getComputedStyle( raw, null ) ){
				matrix = ( style[ X_Node_CSS_VENDER_PREFIX[ 'transform' ] ] || '' ).split( ')' )[ 0 ].split( ', ' );
				x = + ( matrix[ 12 ] || matrix[ 4 ] );
				y = + ( matrix[ 13 ] || matrix[ 5 ] );
				a = matrix[ X_Node_CSS_Support[ 'opacity' ] ];				
			};
		} else
		if( X_Node_CSS_getComputedStyle ){
			if( style = X_Node_CSS_getComputedStyle( raw, null ) ){
				x = parseFloat( style[ 'left' ] );
				y = parseFloat( style[ 'top' ]  );
				a = parseFloat( style[ X_Node_CSS_Support[ 'opacity' ] ] );				
			};
		} else
		if( style = ( raw.currentStyle || raw.style ) ){
			x = parseFloat( style[ 'left' ] );
			y = parseFloat( style[ 'top' ]  );
			a = parseFloat( ( style[ 'filter' ] || 'opacity=1' ).split( 'opacity=' )[ 1 ] );
		};
	};

	return { x : x, y : y, a : a };
};

function X_Node_Anime_onTransitionEnd( e ){
	console.log( '[TransitionEnd] ' + ( this[ '_anime' ] && this[ '_anime' ].phase ) );
	
	if( !this[ '_anime' ] || this[ '_anime' ].phase !== 2 ){
		// ここで return してしまうと、view の更新イベント待ちの場合、アニメが止まる
		X_Node_Anime_reserved && !X_Node_Anime_updateTimerID && !X_Node_updateTimerID && X_Node_Anime_reserveUpdate( X_Node_Anime_reserved = false );
		return X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_STOP_PROPAGATION;
	};
	
	this[ '_anime' ].phase = 3;
	
	X_Node_Anime_clearTransition( this ); // X_EVENT_ANIME_END より前で呼んでおく
	
	X_Node_Anime_onTransition = true;
	this[ 'dispatch' ]( { type : X_EVENT_ANIME_END, 'gpu' : this[ '_anime' ].gpuParent } );
	X_Node_Anime_onTransition = false;
	
	X_Node_Anime_needsDetection = true;
	// iOS は transitionend 内の 更新でアニメーション可能 iOS3, iOS4 で確認 
	// win+Gecko は不可
	X_Node_Anime_updateAnimations( 0, X_Node_updateOnTransitionEnd );
	
	return X_CALLBACK_PREVENT_DEFAULT | X_CALLBACK_STOP_PROPAGATION;
};

function X_Node_Anime_releaseGPULayer(){
	var obj = this[ '_anime' ], tmp;
	if( !obj ){
		// console.log( '_anime無' );
		return;
	};
	X_Node_Anime_clearTransition( this );
	X_Node_Anime_updatePosition( this, obj.destX, obj.destY, obj.destA, false );
	X_Node_ANIMATIONS.splice( X_Node_ANIMATIONS.indexOf( this ), 1 );
	delete obj.gpuTimerID;
	delete this[ '_anime' ];
	//console.log( 'GPUレイヤーの破棄を指示' );
	
	X_ViewPort[ 'listenOnce' ]( X_EVENT_AFTER_UPDATE, this, X_Node_Anime_gpuReleased );
};

function X_Node_Anime_gpuReleased(){
	//console.log( 'GPU レイヤーが解放されました' );
	this[ 'dispatch' ]( { type : X_EVENT_GPU_RELEASED, 'gpu' : true } );
};

function X_Node_Anime_clearTransition( xnode ){
	// 開始座標のセット(新規のみ)
	// アニメーション指定のセット(または解除)(対象のみ)
	// 目標座標のセット
	xnode[ 'unlisten' ]( 'transitionend', X_Node_Anime_onTransitionEnd );

	xnode[ 'css' ]({
		//willChange               : '',
		backfaceVisibility       : '',
		transitionTimingFunction : '',
		transitionDelay          : '',
		transitionDuration       : ''
	});
};

function X_Node_Anime_updatePosition( xnode, x, y, opacity, useGPU ){
	//console.log( 'updatePosition x:' + x + ' gpu:' + !!useGPU );
	if( X_Node_Anime_hasTransform ){
		xnode[ 'css' ]({
			transform : 'translate(' + ( x | 0 ) + 'px,' + ( y | 0 ) + 'px)' + ( useGPU ? X_Node_Anime_translateZ : '' ),
			opacity   : opacity
		});
	} else {
		x === x && xnode[ 'css' ]( 'left', ( x | 0 ) + 'px' );
		y === y && xnode[ 'css' ]( 'top', ( y | 0 ) + 'px' );
		opacity === opacity && xnode[ 'css' ]( 'opacity', opacity );
	};

	if( X_Node_Anime_translateZ ){
		if( useGPU ){
			if( xnode[ '_flags' ] & X_NodeFlags_GPU_RELEASE_RESERVED ){
				xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;
				xnode[ '_flags' ] |= X_NodeFlags_GPU_NOW;
			} else
			if( !( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ) ){
				xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;
				xnode[ '_flags' ] |= X_NodeFlags_GPU_RESERVED;
			};
		} else {
			if( xnode[ '_flags' ] & X_NodeFlags_GPU_NOW ){
				xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;
				xnode[ '_flags' ] |= X_NodeFlags_GPU_RELEASE_RESERVED;
			} else
			if( xnode[ '_flags' ] & X_NodeFlags_GPU_RESERVED ){
				xnode[ '_flags' ] &= X_Node_BitMask_RESET_GPU;
			};
		};		
	};
};

function X_Node_Anime_updateAnimationsNoTransition( e ){
	var i = X_Node_ANIMATIONS.length,
		now = X_Timer_now(),
		obj,
		newX, newY, newA, easing,
		c = false;
	
	for( ; i; ){
		xnode = X_Node_ANIMATIONS[ --i ];
		obj   = xnode[ '_anime' ];

		if( obj.destTime <= now ){
			X_Node_Anime_updatePosition( xnode, obj.destX, obj.destY, obj.destA, false );

			delete xnode[ '_anime' ];
			X_Node_ANIMATIONS.splice( i, 1 );
			//console.log( obj.destA );
			// filter な 親が解除されないと子要素への filter が反映されない
			xnode[ 'asyncDispatch' ]( { type : X_EVENT_ANIME_END, 'gpu' : false } );
		} else {
			easing = obj.easing.fn( ( now - obj.startTime ) / obj.duration );
			newX   = ( obj.destX - obj.startX ) * easing + obj.startX;
			newY   = ( obj.destY - obj.startY ) * easing + obj.startY;
			newA   = ( obj.destA - obj.startA ) * easing + obj.startA;
			X_Node_Anime_updatePosition( xnode, newX, newY, newA, false );
			obj.x = newX;
			obj.y = newY;
			obj.a = newA;
			c = true;
		};
	};
	
	//c && console.log( 'anime... ' + X_Node_updateTimerID );
	
	if( X_Node_Anime_reserved = c ){
		if( X_Node_updateTimerID ){
			// scrollbox では X_System X_EVENT_UPDATED は不可。。。
			!e || e.type !== X_EVENT_UPDATED ?
				X_System[ 'listen' ]( X_EVENT_UPDATED, X_Node_Anime_updateAnimationsNoTransition ) :
				X_Node_Anime_updateTimerID && X_Timer_cancelFrame( X_Node_Anime_updateTimerID );
			X_Node_Anime_updateTimerID = 0;
		} else {
			X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_Node_Anime_updateAnimationsNoTransition );
			X_Node_Anime_updateTimerID = X_Timer_requestFrame( X_Node_Anime_updateAnimationsNoTransition );
		};
	} else {
		X_System[ 'unlisten' ]( X_EVENT_UPDATED, X_Node_Anime_updateAnimationsNoTransition );
		X_Node_Anime_updateTimerID = 0;
	};
};

