X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F02_dom%2F20_XNode.js;h=8ed979d78afa902c63efcc6dd59a4ad93e93e24c;hb=HEAD;hp=04f01e2d7c78ae97a0350dba1c9e505a4450b344;hpb=7648af07e9f87b97d09f4b20015ba95813f3665b;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/02_dom/20_XNode.js b/0.6.x/js/02_dom/20_XNode.js index 04f01e2..8ed979d 100644 --- a/0.6.x/js/02_dom/20_XNode.js +++ b/0.6.x/js/02_dom/20_XNode.js @@ -8,8 +8,7 @@ */ var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( 'X.Node', - X_Class.POOL_OBJECT, - + X_Class.NONE, { /** * 要素に振られるユニークID @@ -152,10 +151,12 @@ var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( * TODO Node の継承ができない! */ 'Constructor' : function( v ){ + // TODO uid = X_Node_CHASHE.indexOf( null ), uid === -1 ? X_Node_CHASHE.length : uid; var uid = X_Node_CHASHE.length, css, xnodes, xnode, parent; - if( X_Node_newByTag ){ + // TODO 継承クラスで this.Super('DIV') ができない! -> サブクラスから呼ばれた場合も + if( X_Node_newByTag || this.constructor !== Node ){ X_Node_newByTag = false; this[ '_tag' ] = v.toUpperCase(); arguments[ 1 ] && this[ 'attr' ]( arguments[ 1 ] ); @@ -223,6 +224,7 @@ var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( X_Node_CHASHE[ this[ '_uid' ] = uid ] = this; }, + // TODO .mesure() -> X.Event.MESURED 'width' : X_Node_width, 'height' : X_Node_height, 'clientWidth' : X_Node_clientWidth, @@ -253,6 +255,8 @@ var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( 'createTextAt' : X_Node_createTextAt, + 'createRange' : X_Node_createRange, + 'clone' : X_Node_clone, 'append' : X_Node_append, @@ -289,7 +293,7 @@ var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( 'toggleClass' : X_Node_toggleClass, 'hasClass' : X_Node_hasClass, - 'html' : X_Node_html, + 'html' : X_Node__html, 'text' : X_Node_text, 'call' : X_Node_call, 'each' : X_Node_each @@ -360,6 +364,7 @@ function X_Node_getType( v ){ if( X_Type_isString( v ) ){ return '<' === v.charAt( 0 ) && v.charAt( v.length - 1 ) === '>' ? X_NodeType_HTML_STRING : X_NodeType_STRING; }; + // Node サブクラスのインスタンス if( v[ 'instanceOf' ] && v[ 'instanceOf' ]( Node ) ) return X_NodeType_XNODE; return 0; }; @@ -383,7 +388,7 @@ function X_Node_getXNode( v ){ case X_NodeType_RAW_TEXT : if( v[ 'UID' ] ) return X_Node_CHASHE[ v[ 'UID' ] ]; for( chashe = X_Node_CHASHE, i = chashe.length; i; ){ - if( ( xnode = X_Node_CHASHE[ --i ] ) && ( xnode[ '_rawObject' ] === v ) ) return xnode; + if( ( xnode = chashe[ --i ] ) && ( xnode[ '_rawObject' ] === v ) ) return xnode; }; }; }; @@ -394,11 +399,10 @@ function X_Node_getRoot( xnode ){ }; - +// TODO document.all[ uid ] -> document[ uid ] var X_Node__ie4getRawNode = X_UA_DOM.IE4 && function ( that ){ return that[ '_rawObject' ] || - ( that[ '_rawObject' ] = document.all[ 'ie4uid' + that[ '_uid' ] ] ) || - ( that[ '_id' ] && ( that[ '_rawObject' ] = document.all[ that[ '_id' ] ] ) ); + ( that[ '_rawObject' ] = document.all[ 'ie4uid' + that[ '_uid' ] ] || that[ '_id' ] && document.all[ that[ '_id' ] ] ); }; @@ -446,6 +450,7 @@ function X_Node_toggleInGPUFlag( gpuRoot, xnodes, flag ){ */ function X_Node_create( tag, opt_attrs, opt_css ){ var xnode; + if( !this[ '_tag' ] ) return; this[ 'append' ]( xnode = X_Doc_create( tag, opt_attrs, opt_css ) ); return xnode; @@ -494,6 +499,15 @@ function X_Node_createTextAt( index, text ){ }; /** + * 選択されたテキストへの参照やテキスト座標情報 + * @alias Node.prototype.createRange + * @return {TextRange} 新規作成されたテキストレンジ + */ +function X_Node_createRange( a, b, c ){ + return X_TextRange( this, a, b, c ); +}; + +/** * Node のクローンを作成し返す。id もクローンされる点に注意。イベントリスナはクローンされない。 * http://d.hatena.ne.jp/think49/20110724/1311472811 * http://d.hatena.ne.jp/uupaa/20100508/1273299874 @@ -503,17 +517,26 @@ function X_Node_createTextAt( index, text ){ */ function X_Node_clone( opt_clone_children ){ var xnode, xnodes, i, l; + if( this[ '_tag' ] ){ X_Node_newByTag = true; - xnode = Node( this[ '_tag' ], X_Object_clone( this[ '_attrs' ] ), X_Object_clone( this[ '_css' ] ) ) + xnode = Node( this[ '_tag' ], X_Object_copy( this[ '_attrs' ] ), X_Object_copy( this[ '_css' ] ) ) [ 'attr' ]( { 'id' : this[ '_id' ] } ) [ 'className' ]( this[ '_className' ] ); + + if( this[ '_flags' ] & X_NodeFlags_IS_SVG ){ + xnode[ '_flags' ] |= X_NodeFlags_IS_SVG; + }; + if( this[ '_flags' ] & X_NodeFlags_IS_VML ){ + xnode[ '_flags' ] |= X_NodeFlags_IS_VML; + }; + if( opt_clone_children && ( xnodes = this[ '_xnodes' ] ) && ( l = xnodes.length ) ){ for( i = 0; i < l; ++i ){ xnode[ 'append' ]( xnodes[ i ][ 'clone' ]( true ) ); }; }; - return xnode; + return xnode; }; X_Node_newByText = true; return Node( this[ '_text' ] ); @@ -551,9 +574,10 @@ function X_Node_append( v ){ return X_Node_append.apply( this, X_HtmlParser_parse( v, true ) ); case X_NodeType_XNODE : // 親の xnodes から v を消す - v.parent && v[ 'remove' ](); + if( v.parent === this && xnodes[ xnodes.length - 1 ] === v ) return this; + v[ 'remove' ](); // IE4 でテキストノードの追加、FIXED 済でない場合、親に要素の追加を通知 - if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( this[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) this[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; + if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( ( this[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) ) this[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; break; default : return this; @@ -566,6 +590,12 @@ function X_Node_append( v ){ v[ '_xnodes' ] && X_Node_toggleInTreeFlag( v[ '_xnodes' ], true ); X_Node_reserveUpdate(); }; + if( this[ '_flags' ] & X_NodeFlags_IS_SVG ){ + v[ '_flags' ] |= X_NodeFlags_IS_SVG; + }; + if( this[ '_flags' ] & X_NodeFlags_IS_VML ){ + v[ '_flags' ] |= X_NodeFlags_IS_VML; + }; return this; }; @@ -623,7 +653,7 @@ function X_Node_appendAt( start, v ){ v[ 'remove' ](); }; // IE4 でテキストノードの追加、FIXED 済でない場合、親に要素の追加を通知 - if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( this[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) this[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; + if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( ( this[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) ) this[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; break; default : return this; @@ -636,6 +666,12 @@ function X_Node_appendAt( start, v ){ v[ '_xnodes' ] && X_Node_toggleInTreeFlag( v[ '_xnodes' ], true ); X_Node_reserveUpdate(); }; + if( this[ '_flags' ] & X_NodeFlags_IS_SVG ){ + v[ '_flags' ] |= X_NodeFlags_IS_SVG; + }; + if( this[ '_flags' ] & X_NodeFlags_IS_VML ){ + v[ '_flags' ] |= X_NodeFlags_IS_VML; + }; return this; }; @@ -747,10 +783,11 @@ function X_Node_swap( v ){ }; /** - * 要素を抜く。 + * 要素を親要素から抜く。jQuery の remove と異なり、インスタンスは破壊(kill)されず、再び別の親に挿入等できる * @alias Node.prototype.remove * @return {Node} 自身。チェインメソッド * @example node.remove(); + * parent.append( node ); 新しい親に追加できる */ function X_Node_remove(){ var parent = this.parent, @@ -758,6 +795,14 @@ function X_Node_remove(){ if( !parent ) return this; + // stop() -> + if( this[ '_anime' ] && this[ '_anime' ].phase ){ + console.log( 'Animation 中の REMOVE' ); + X_NodeAnime_stopNow( this ); + }; + // 子孫にアニメーション中の要素が要る + // 先祖に GPU 化した要素が要る + delete this.parent; parent[ '_xnodes' ].splice( parent[ '_xnodes' ].indexOf( this ), 1 ); @@ -766,12 +811,11 @@ function X_Node_remove(){ this[ '_xnodes' ] && X_Node_toggleInTreeFlag( this[ '_xnodes' ], false ); if( X_UA_DOM.IE4 ){ - elm = this[ '_rawObject' ] || X_Node__ie4getRawNode( this ); - if( elm ){ + if( elm = this[ '_rawObject' ] || X_Node__ie4getRawNode( this ) ){ X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; - X_Node_reserveUpdate(); + X_Node_reserveUpdate(); } else - if( !this[ '_tag' ] && ( parent[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ){ + if( !this[ '_tag' ] && ( ( parent[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) ){ parent[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; }; } else { @@ -781,6 +825,14 @@ function X_Node_remove(){ X_Node_reserveUpdate(); }; }; + } else { + if( !X_UA_DOM.IE4 ){ + elm = this[ '_rawObject' ]; + if( elm && elm.parentNode && elm.parentNode.tagName ){ + X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; + X_Node_reserveUpdate(); + }; + }; }; return this; }; @@ -793,7 +845,9 @@ function X_Node_remove(){ */ function X_Node_empty(){ var xnodes = this[ '_xnodes' ], i; + if( xnodes && ( i = xnodes.length ) ){ + delete this[ '_xnodes' ]; for( ; i; ){ xnodes[ --i ][ 'kill' ](); }; @@ -808,31 +862,38 @@ function X_Node_onKill( that ){ if( ( that[ '_flags' ] & X_NodeFlags_EXIST ) === 0 ) return; - parent && parent[ '_xnodes' ].splice( parent[ '_xnodes' ].indexOf( that ), 1 ); - - elm = that[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( that ); - // elm && that[ '_listeners' ] && X_EventDispatcher_unlistenAll( that ); // イベントの退避 + parent && parent[ '_xnodes' ] && parent[ '_xnodes' ].splice( parent[ '_xnodes' ].indexOf( that ), 1 ); if( xnodes && ( i = xnodes.length ) ){ + delete that[ '_xnodes' ]; for( ; i; ){ xnodes[ --i ][ 'kill' ](); }; + xnodes.length = 0; + }; + + X_Node_CHASHE[ that[ '_uid' ] ] = null; // array に対して delete X_Node_CHASHE[ uid ] はまずい! + + if( that[ '_anime' ] && that[ '_anime' ].phase ){ + console.log( 'Animation 中の KILL' ); + X_NodeAnime_stopNow( that ); }; - delete X_Node_CHASHE[ that[ '_uid' ] ]; + elm = that[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( that ); + // remove _xnodes if( X_UA_DOM.IE4 ){ if( elm ){ X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = elm; X_Node_reserveUpdate(); } else - if( !that[ '_tag' ] && ( parent[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ){ + if( !that[ '_tag' ] && ( ( parent[ '_flags' ] & X_NodeFlags_IE4_FIXED ) === 0 ) ){ parent[ '_flags' ] |= X_NodeFlags_IE4_DIRTY_CHILDREN; }; } else { if( elm && elm.parentNode && elm.parentNode.tagName ){ X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = elm; - X_Node_reserveUpdate(); + X_Node_reserveUpdate(); }; }; }; @@ -848,21 +909,15 @@ function X_Node_onKill( that ){ * @example node.contains( testNode ); */ function X_Node_contains( v ){ - var elm, type, xnodes, i; + var xnodes; + if( !v || !this[ '_tag' ] || this === v ) return false; - // contains ie4+ - if( ( elm = this[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ) ) && document.contains && ( type = X_Node_getType( v ) ) && ( type === X_NodeType_RAW_HTML || type === X_NodeType_RAW_TEXT ) ){ - return elm.contains( v ); - }; - //if( document.compareDocumentPosition ){ - // - //}; xnodes = this[ '_xnodes' ]; if( !xnodes || !xnodes.length ) return false; - if( xnodes.indexOf( v ) !== -1 ) return true; // fast - for( i = xnodes.length; i; ){ - if( xnodes[ --i ][ 'contains' ]( v ) ) return true; + + while( v = v[ 'parent' ] ){ + if( this === v ) return true; }; return false; }; @@ -1062,12 +1117,17 @@ function X_Node_hasClass( v ){ * @return {string|Node} * @example node.html( '' ); */ -function X_Node_html( html ){ +function X_Node__html( html ){ var _ = '', q = '"', xnodes, n, i, l; // setter if( html !== undefined ){ // String 以外に Number や false null なども許可 if( !this[ '_tag' ] ) return this[ 'text' ]( html ); - return html ? this[ 'empty' ]()[ 'append' ].apply( this, X_HtmlParser_parse( html, true ) ) : this[ 'empty' ](); + + this[ 'empty' ](); + if( html += '' ){ + X_Node_append.apply( this, X_HtmlParser_parse( html, true ) ); + }; + return this; }; // getter @@ -1145,14 +1205,28 @@ function X_Node_text( text ){ return this[ '_text' ]; }; -/* +/** * HTML要素に対して name の関数を実行しその戻り値を返す。関数に渡す引数も任意に設定できる。 + * @alias Node.prototype.call + * @param {string} [name] 要素の関数名 + * @return {*} + * @example node.call( 'focus' ); */ function X_Node_call( name /*, opt_args... */ ){ - var l = arguments.length - 1, - v, raw, func, args, params, i; + var args = arguments, + l = args.length - 1, + v, raw, parent, body, + child, childX, childY, childW, childH, + parentW, parentH, + parentSX, parentSY, parentSW, parentSH, + visibleX, visibleY, visibleW, visibleH, + visiblePartX, visiblePartY, func, args, params, i; switch( name ){ + case 'isSVG' : + return !!( this[ '_flags' ] & X_NodeFlags_IS_SVG ); + case 'isVML' : + return !!( this[ '_flags' ] & X_NodeFlags_IS_VML ); case 'nodeType' : return this[ '_tag' ] ? 1 : 3; case 'outerHTML' : @@ -1163,36 +1237,105 @@ function X_Node_call( name /*, opt_args... */ ){ case 'treeIsDirty' : return !!X_Node_updateTimerID; case 'fontSize' : - return X_Node_CSS_getCharSize( this ); + return ( this[ '_flags' ] & X_NodeFlags_IN_TREE ) ? X_Node_CSS_getCharSize( this ) : 0; case 'inGPU' : return !!( this[ '_flags' ] & ( X_NodeFlags_GPU_NOW | X_NodeFlags_GPU_RELEASE_RESERVED ) ); + case 'isGPUChild' : + if( this[ '_flags' ] & X_NodeFlags_IN_TREE ){ + parent = this; + while( parent = parent.parent ){ + if( parent[ '_flags' ] & ( X_NodeFlags_GPU_NOW | X_NodeFlags_GPU_RELEASE_RESERVED ) ) return true; + }; + }; + return false; + case 'containGPU' : + + return false; + case 'canAnimateNow' : + return ( this[ '_flags' ] & X_NodeFlags_IN_TREE ) && X_NodeAnime_detectWaitAnimation( this, true, true ) === 6; + case 'animeState' : + return this[ '_anime' ] ? this[ '_anime' ].phase : 0; + case 'animeProgress' : + return this[ '_anime' ] && this[ '_anime' ].phase === 7 ? this[ '_anime' ].progress : 0; }; X_Node_updateTimerID && X_Node_startUpdate(); raw = this[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); + if( !raw ) return; if( name === 'scrollTo' ){ - raw.scrollLeft = arguments[ 1 ] || 0; - raw.scrollTop = arguments[ 2 ] || 0; + raw.scrollLeft = args[ 1 ] || 0; + raw.scrollTop = args[ 2 ] || 0; return; }; + if( name === 'inView' ){ + if( !( this[ '_flags' ] & X_NodeFlags_IN_TREE ) ) return { 'isInView' : false }; + body = X_elmBody; + child = raw; + visibleX = visibleY = visibleW = visibleH = 0; + while( child !== body ){ + parent = child.parentNode || child.parentElement; + parentH = parent.clientHeight; + parentW = parent.clientWidth; + parentSW = parent.scrollHeight; + parentSH = parent.scrollWidth; + // 親がスクロール領域を持つ + if( parentH < parentSH || parentW < parentSW ){ + childX = child.offsetLeft + visibleX; + childY = child.offsetTop + visibleY; + childW = visibleW || child.offsetWidth; + childH = visibleH || child.offsetHeight; + parentSX = parent.scrollLeft; + parentSY = parent.scrollTop; + // 子が表示領域内 + if( parentSY < childY + childH && + childY < parentSY + parentH && + parentSX < childX + childW && + childX < parentSX + parentW ){ + + // right:子の左側が見えている left:子の左側が見えている both:完全に見えている + visiblePartX = + childX < parentSX ? 'right' : + ( parentSX + parentW ) < ( childX + childW ) ? 'left' : 'both'; + visiblePartY = + childY < parentSY ? 'bottom' : + ( parentSY + parentH ) < ( childY + childH ) ? 'top' : 'both'; + + // 子が見える領域 + visibleX = visiblePartX === 'right' ? 0 : childX - parentSX; + visibleY = visiblePartX === 'bottom' ? 0 : childY - parentSY; + visibleW = + visiblePartX === 'both' ? childW : + visiblePartX === 'right' ? ( parentSX + parentW - childX ) : ( childX + childW - parentSX ); + visibleH = + visiblePartY === 'both' ? childH : + visiblePartY === 'bottom' ? ( parentSY + parentH - childY ) : ( childY + childH - parentSY ); + } else { + return { 'isInView' : false }; + }; + }; + child = parent; + }; + return { 'isInView' : true }; + }; + func = raw[ name ]; if( X_Type_isFunction( func ) ){ if( l ){ - args = X_Object_cloneArray( arguments ); + args = X_Array_copy( args ); args.shift(); return func.apply( raw, args ); }; return raw[ name ](); } else - if( X_Type_isUnknown( func ) ){ + if( X_UA[ 'IE' ] < 9 && ( X_Type_isUnknown( func ) || X_Type_isObject( func ) ) ){ // typeof func === unknown に対策 // http://la.ma.la/blog/diary_200509031529.htm if( l ){ - args = X_Object_cloneArray( arguments ); + args = X_Array_copy( args ); args.shift(); params = []; @@ -1216,7 +1359,7 @@ function X_Node_call( name /*, opt_args... */ ){ function X_Node_each( func /*, opt_args */ ){ var args; if( 1 < arguments.length ){ - args = X_Object_cloneArray( arguments ); + args = X_Array_copy( arguments ); args[ 0 ] = 0; func.apply( this, args ); } else { @@ -1239,7 +1382,7 @@ function X_Node_reserveUpdate(){ var X_Node_updateReservedByReleaseGPU = false; function X_Node_startUpdate( time ){ - var removal, i, xnodeOrElm; + var removal, i, xnodeOrElm, xnodesIEFilterFixAfter, xnode, active; if( !X_Node_updateTimerID || X_ViewPort_readyState < X_EVENT_INIT ){ return; @@ -1259,6 +1402,7 @@ function X_Node_startUpdate( time ){ for( ; i; ){ xnodeOrElm = removal[ --i ]; // TODO GPU レイヤーの子の場合、remove をスキップする。 非GPU レイヤーへ apppend される場合、clone する? + // GPU レイヤーを削除する場合、cssText = '' して GPU レイヤーを解除する if( !xnodeOrElm[ 'instanceOf' ] ){ if( X_UA_DOM.IE4 ){ xnodeOrElm.removeAttribute( 'id' ); // ? @@ -1278,11 +1422,23 @@ function X_Node_startUpdate( time ){ removal.length = 0; }; + // 強制的に再描画を起こす, 但し activeElement からフォーカスが外れるため復帰する + // IE5mode win10 で 確認 + if( X_UA[ 'IE5' ] ){ + active = FocusUtility_getFocusedElement(); + X_elmBody.style.visibility = 'hidden'; + }; + if( X_Node_html[ '_flags' ] & X_Node_BitMask_IS_DIRTY ){ - X_Node__commitUpdate( X_Node_html, X_Node_html[ '_rawObject' ].parentNode, null, X_Node_html[ '_flags' ] ); + X_Node__commitUpdate( X_Node_html, X_Node_html[ '_rawObject' ].parentNode, null, X_Node_html[ '_flags' ], 1, xnodesIEFilterFixAfter = [] ); } else { - X_Node__commitUpdate( X_Node_head, X_Node_head[ '_rawObject' ].parentNode, null, X_Node_head[ '_flags' ] ); - X_Node__commitUpdate( X_Node_body, X_Node_body[ '_rawObject' ].parentNode, null, X_Node_body[ '_flags' ] ); + X_Node__commitUpdate( X_Node_head, X_Node_head[ '_rawObject' ].parentNode, null, X_Node_head[ '_flags' ], 1, xnodesIEFilterFixAfter = [] ); + X_Node__commitUpdate( X_Node_body, X_Node_body[ '_rawObject' ].parentNode, null, X_Node_body[ '_flags' ], 1, xnodesIEFilterFixAfter = [] ); + }; + + if( X_UA[ 'IE5' ] ){ + X_elmBody.style.visibility = ''; + active && active.parentNode && FocusUtility_setTemporarilyFocus( active ); }; if( X_Node_updateReservedByReleaseGPU ){ @@ -1290,12 +1446,16 @@ function X_Node_startUpdate( time ){ X_Node_updateReservedByReleaseGPU = false; }; - if( time ){ - // X.Timer 経由でないと発火しない このイベントでサイズを取ると無限ループに - X_System[ '_listeners' ] && X_System[ '_listeners' ][ X_EVENT_UPDATED ] && X_System[ 'dispatch' ]( X_EVENT_UPDATED ); - } else { - X_System[ '_listeners' ] && X_System[ '_listeners' ][ X_EVENT_UPDATED ] && X_System[ 'asyncDispatch' ]( X_EVENT_UPDATED ); + if( X_NodeFlags_IE_FILTER_FIX_AFTER && xnodesIEFilterFixAfter.length ){ + for( i = 0; xnode = xnodesIEFilterFixAfter[ i ]; ++i ){ + xnode[ '_flags' ] &= ~X_NodeFlags_IE_FILTER_FIX_AFTER; + X_Node_CSS_onAfterUpdateIEFilterFix( xnode ); + }; }; + + // time を視て X.Timer 経由の場合、即座に発火する。 + // width() 等で強制的にツリーを構築している場合、UPDATE イベントのコールバックで要素を変更しサイズを取ると無限ループになる,これを防ぐため asyncDispatch とする + X_System[ '_listeners' ] && X_System[ '_listeners' ][ X_EVENT_UPDATED ] && X_System[ time ? 'dispatch' : 'asyncDispatch' ]( X_EVENT_UPDATED ); X_ViewPort[ '_listeners' ] && X_ViewPort[ '_listeners' ][ X_EVENT_AFTER_UPDATE ] && X_ViewPort[ 'asyncDispatch' ]( X_EVENT_AFTER_UPDATE ); }; @@ -1315,28 +1475,28 @@ function X_Node_startUpdate( time ){ */ var X_Node__commitUpdate = X_UA_DOM.W3C ? - ( function( that, parentElement, nextElement, accumulatedFlags ){ + ( function( that, parentElement, nextElement, accumulatedFlags, ie8AccumulatedOpcity, xnodesIEFilterFixAfter ){ var elm = that[ '_rawObject' ], - created, xnodes, l, next; + created, xnodes, l, next, anime, v, currentOpcity; // 1. GPU 一切の更新をスキップ if( that[ '_flags' ] & X_NodeFlags_GPU_NOW ){ - console.log( '更新のskip ' + !!( that[ '_flags' ] & X_Node_BitMask_IS_DIRTY ) ); + console.log( '更新のskip ' + that[ '_className' ] + !!( that[ '_flags' ] & X_Node_BitMask_IS_DIRTY ) ); that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); return elm; }; // 2. GPU解放予約 if( that[ '_flags' ] & X_NodeFlags_GPU_RELEASE_RESERVED ){ - // console.log( 'GPU 解放 ' ); + console.log( 'GPU 解放 ' ); //X_Node_updateReservedByReleaseGPU = true; - //X_Node__updateRawNode( that, elm ); that[ '_flags' ] &= X_Node_BitMask_RESET_GPU; - //return elm;// TODO もしかしたらこのタイミングで更新できるかも。 + //return elm;// このタイミングで更新できるっぽい。 }; // 3. GPU予約 -> GPU if( that[ '_flags' ] & X_NodeFlags_GPU_RESERVED ){ + // TODO size 取得のための update の場合、GPU化をスキップ that[ '_flags' ] &= X_Node_BitMask_RESET_GPU; that[ '_flags' ] |= X_NodeFlags_GPU_NOW; }; @@ -1345,7 +1505,8 @@ var X_Node__commitUpdate = if( that[ '_flags' ] & X_NodeFlags_STYLE_IS_DISPLAY_NONE ){ if( X_Node_displayNoneFixForIE5 ){ // filter の効いている要素を含む要素は display:none が無視される。 - // filter = '' で削除はできるが、再表示時に filter が消える。 -> filter な要素を削除してしまう。 + // filter = '' で削除はできるが、再表示時に filter が消える。 -> filter な要素を削除してしまう。 + // TODO filters[0].enabled = false なんてどう? if( elm && elm.parentNode ){ X_Node__actualRemove( that ); }; @@ -1359,7 +1520,7 @@ var X_Node__commitUpdate = accumulatedFlags |= that[ '_flags' ]; if( that[ '_flags' ] & X_NodeFlags_IE5_DISPLAY_NONE_FIX ){ - if( accumulatedFlags & ( X_NodeFlags_DIRTY_POSITION | X_NodeFlags_DIRTY_ID | X_NodeFlags_DIRTY_CLASSNAME ) === 0 ){ + if( ( accumulatedFlags & ( X_NodeFlags_DIRTY_POSITION | X_NodeFlags_DIRTY_ID | X_NodeFlags_DIRTY_CLASSNAME ) ) === 0 ){ return nextElement; }; }; @@ -1368,7 +1529,12 @@ var X_Node__commitUpdate = if( !elm ){ if( !that[ '_tag' ] ){ that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; - that[ '_rawObject' ] = elm = document.createTextNode( X_String_chrReferanceTo( that[ '_text' ] ) ); + if( X_UA[ 'IE' ] < 8 ){ + // \n -> \r\n に変換しないと pre タグで改行されない win10ie7(ie11 emu) で確認 + elm = document.createTextNode( X_String_chrReferanceTo( that[ '_text' ] ).split( '\n' ).join( X_String_CRLF ) ); + } else { + elm = document.createTextNode( X_String_chrReferanceTo( that[ '_text' ] ) ); + }; if( !X_UA[ 'IE' ] ){ elm[ 'UID' ] = that[ '_uid' ]; }; @@ -1376,7 +1542,7 @@ var X_Node__commitUpdate = if( X_Node_strictElmCreation ){ that[ '_flags' ] & X_NodeFlags_OLD_CSSTEXT && X_Node_CSS_objToCssText( that, true ); // OLD_CSSTEXT ?? - that[ '_rawObject' ] = elm = + elm = document.createElement( [ '<', that[ '_tag' ], ' UID="', that[ '_uid' ], '"', @@ -1385,10 +1551,17 @@ var X_Node__commitUpdate = X_Node_Attr_objToAttrText( that, true ), that[ '_cssText' ] ? ' style="' + that[ '_cssText' ] + '"' : '', '>' ].join( '' ) ); + } else + if( that[ '_flags' ] & X_NodeFlags_IS_SVG ){ + elm = document.createElementNS( 'http://www.w3.org/2000/svg', that[ '_tag' ].toLowerCase() ); + + // TODO math http://www.w3.org/1998/Math/MathML } else { - that[ '_rawObject' ] = elm = document.createElement( that[ '_tag' ] ); + elm = document.createElement( that[ '_tag' ] ); }; + that[ '_rawObject' ] = elm; + // IE には要素追加のタイミングで起こるメモリリークがありここで追加 if( !X_Node_addTreeAfterChildren ){ nextElement ? @@ -1400,10 +1573,10 @@ var X_Node__commitUpdate = X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 that[ '_flags' ] |= X_NodeFlags_ACTUAL_LISTENING; - if( X_Node_documentFragment ){ + //if( X_Node_documentFragment ){ //( frg = X_Node_documentFragment ).appendChild( elm ); // 連続する要素の差し替えの場合に有効 - }; + //}; if( X_Node_strictElmCreation ){ that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; @@ -1436,15 +1609,19 @@ var X_Node__commitUpdate = parentElement.appendChild( elm ); }; - if( that[ '_listeners' ] && ( that[ '_flags' ] & X_NodeFlags_ACTUAL_LISTENING ) === 0 ){ + if( that[ '_listeners' ] && ( ( that[ '_flags' ] & X_NodeFlags_ACTUAL_LISTENING ) === 0 ) ){ X_EventDispatcher_toggleAllEvents( that, true );// イベントの退避 that[ '_flags' ] |= X_NodeFlags_ACTUAL_LISTENING; }; + // ie8 では子要素に opacity が反映されないため、親要素の opacity を積算する + currentOpcity = that[ '_css' ] && 0 <= that[ '_css' ].opacity && that[ '_css' ].opacity; + ie8AccumulatedOpcity = ie8AccumulatedOpcity * ( currentOpcity || 1 ); + // 8. 更新の適用 if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ){ delete that[ '_fontSize' ]; - X_Node__updateRawNode( that, elm ); + X_Node__updateRawNode( that, elm, currentOpcity, ie8AccumulatedOpcity, accumulatedFlags ); }; // 9. ie5 only @@ -1461,10 +1638,14 @@ var X_Node__commitUpdate = }; }; + if( that[ '_flags' ] & X_NodeFlags_IE_FILTER_FIX_AFTER ){ + xnodesIEFilterFixAfter[ xnodesIEFilterFixAfter.length ] = that; + }; + // 10. 子要素の更新。 if( ( xnodes = that[ '_xnodes' ] ) && ( l = xnodes.length ) ) { for( ; l; ){ - next = X_Node__commitUpdate( xnodes[ --l ], elm, next, accumulatedFlags ); + next = X_Node__commitUpdate( xnodes[ --l ], elm, next, accumulatedFlags, ie8AccumulatedOpcity, xnodesIEFilterFixAfter ); }; }; @@ -1479,12 +1660,34 @@ var X_Node__commitUpdate = }; }; +/* + * if( ( anime = that[ '_anime' ] ) && 6 <= anime.phase && anime.doScroll ){ + if( anime.phase === 6 ){ + v = anime.fromScrollX; + if( v === v ){ + elm.scrollLeft = v; + } else { + anime.fromScrollX = elm.scrollLeft; + }; + v = anime.fromScrollY; + if( v === v ){ + elm.scrollTop = v; + } else { + anime.fromScrollY = elm.scrollTop; + }; + } else { + elm.scrollLeft = anime.scrollX; + elm.scrollTop = anime.scrollY; + }; + }; + */ + return elm; }) : X_UA_DOM.IE4 ? ( function( that, parentElement, prevElement, accumulatedFlags ){ var elm = that[ '_rawObject' ] || X_Node__ie4getRawNode( that ), - xnodes, l, i, dirty, mix, html, text, prev; + xnodes, l, i, dirty, mix, html, text, prev, anime, v; if( !that[ '_tag' ] ){ that[ '_flags' ] & X_NodeFlags_DIRTY_CONTENT && X_Node__updateRawNode( that, elm ); @@ -1508,68 +1711,90 @@ var X_Node__commitUpdate = prevElement.insertAdjacentHTML( 'AfterEnd', X_Node__actualCreate( that, false ) ) : parentElement.insertAdjacentHTML( 'AfterBegin', X_Node__actualCreate( that, false ) ); X_Node__afterActualCreate( that ); - return that[ '_rawObject' ] || X_Node__ie4getRawNode( that ); - }; - - accumulatedFlags |= that[ '_flags' ]; - - xnodes = that[ '_xnodes' ]; - l = xnodes ? xnodes.length : 0; - dirty = !!( that[ '_flags' ] & X_NodeFlags_IE4_DIRTY_CHILDREN ); - - /* - * HTML の下に TextNode だけ 。MIX_FIXED でない場合、削除、追加 を親に通知 - * HTML の下に HTML だけ - * HTML の下は MIX -> TextNode, html の削除、変更、追加 - * HTML の下は MIX_FIXED -> TextNode を に置き換えてあるのでW3C DON 的に触ることができる - */ - if( dirty ){ - that[ '_flags' ] &= ~X_NodeFlags_IE4_DIRTY_CHILDREN; - for( i = 0; i < l; ++i ){ - if( xnodes[ i ][ '_tag' ] ){ - that[ '_flags' ] |= X_NodeFlags_IE4_HAS_ELEMENT; - } else { - that[ '_flags' ] |= X_NodeFlags_IE4_HAS_TEXTNODE; + + elm = that[ '_rawObject' ] || X_Node__ie4getRawNode( that ); + } else { + accumulatedFlags |= that[ '_flags' ]; + + xnodes = that[ '_xnodes' ]; + l = xnodes ? xnodes.length : 0; + dirty = !!( that[ '_flags' ] & X_NodeFlags_IE4_DIRTY_CHILDREN ); + + /* + * HTML の下に TextNode だけ 。MIX_FIXED でない場合、削除、追加 を親に通知 + * HTML の下に HTML だけ + * HTML の下は MIX -> TextNode, html の削除、変更、追加 + * HTML の下は MIX_FIXED -> TextNode を に置き換えてあるのでW3C DON 的に触ることができる + */ + if( dirty ){ + that[ '_flags' ] &= ~X_NodeFlags_IE4_DIRTY_CHILDREN; + for( i = 0; i < l; ++i ){ + if( xnodes[ i ][ '_tag' ] ){ + that[ '_flags' ] |= X_NodeFlags_IE4_HAS_ELEMENT; + } else { + that[ '_flags' ] |= X_NodeFlags_IE4_HAS_TEXTNODE; + }; + if( that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_BitMask_IE4_IS_MIX ){ + mix = true; + break; + }; }; - if( that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_BitMask_IE4_IS_MIX ){ - mix = true; - break; + }; + + if( that[ '_flags' ] & X_NodeFlags_IE4_FIXED || that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_NodeFlags_IE4_HAS_ELEMENT ){ + for( i = 0; i < l; ++i ){ + prev = X_Node__commitUpdate( xnodes[ i ], elm, prev, accumulatedFlags ); }; + } else + if( mix ){ + html = []; + for( i = 0; i < l; ++i ){ + html[ i ] = X_Node__actualCreate( xnodes[ i ], false ); + }; + elm.innerHTML = html.join( '' ); + for( i = 0; i < l; ++i ){ + X_Node__afterActualCreate( xnodes[ i ] ); + }; + that[ '_flags' ] |= X_NodeFlags_IE4_FIXED; + } else + if( that[ '_flags' ] & X_NodeFlags_IE4_HAS_TEXTNODE ){ + dirty = dirty || false; + for( i = 0; i < l; ++i ){ + text = xnodes[ i ]; + if( text[ '_flags' ] & X_Node_BitMask_IS_DIRTY ){ + text[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; + dirty = true; + }; + }; + if( dirty ) elm.innerHTML = that[ 'text' ](); }; + + if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ) delete that[ '_fontSize' ]; + + that[ '_flags' ] &= ~X_NodeFlags_DIRTY_POSITION; + that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); }; - if( that[ '_flags' ] & X_NodeFlags_IE4_FIXED || that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_NodeFlags_IE4_HAS_ELEMENT ){ - for( i = 0; i < l; ++i ){ - prev = X_Node__commitUpdate( xnodes[ i ], elm, prev, accumulatedFlags ); - }; - } else - if( mix ){ - html = []; - for( i = 0; i < l; ++i ){ - html[ i ] = X_Node__actualCreate( xnodes[ i ], false ); - }; - elm.innerHTML = html.join( '' ); - for( i = 0; i < l; ++i ){ - X_Node__afterActualCreate( xnodes[ i ] ); - }; - that[ '_flags' ] |= X_NodeFlags_IE4_FIXED; - } else - if( that[ '_flags' ] & X_NodeFlags_IE4_HAS_TEXTNODE ){ - dirty = dirty || false; - for( i = 0; i < l; ++i ){ - text = xnodes[ i ]; - if( text[ '_flags' ] & X_Node_BitMask_IS_DIRTY ){ - text[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; - dirty = true; + if( ( anime = that[ '_anime' ] ) && 6 <= anime.phase && anime.doScroll ){ + if( anime.phase === 6 ){ + v = anime.fromScrollX; + if( v === v ){ + elm.scrollLeft = v; + } else { + anime.fromScrollX = elm.scrollLeft; + }; + v = anime.fromScrollY; + if( v === v ){ + elm.scrollTop = v; + } else { + anime.fromScrollY = elm.scrollTop; }; + } else { + elm.scrollLeft = anime.scrollX; + elm.scrollTop = anime.scrollY; }; - if( dirty ) elm.innerHTML = that[ 'text' ](); }; - - if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ) delete that[ '_fontSize' ]; - - that[ '_flags' ] &= ~X_NodeFlags_DIRTY_POSITION; - that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); + return elm; }) : (function(){}); @@ -1579,22 +1804,39 @@ var X_Node__commitUpdate = */ var X_Node__updateRawNode = X_UA_DOM.W3C ? - ( function( that, elm ){ - var attrs, rename, k, v; + ( function( that, elm, currentOpcity, ie8AccumulatedOpcity, accumulatedFlags ){ + var // flags = that[ '_flags' ], + attrs, rename, k, v, f; // textNode if( !that[ '_tag' ] ){ - elm.data = X_String_chrReferanceTo( that[ '_text' ] ); + if( X_UA[ 'IE' ] < 8 ){ + // \n -> \r\n に変換しないと pre タグで改行されない win10ie7(ie11 emu) で確認 + elm.data = X_String_chrReferanceTo( that[ '_text' ] ).split( '\n' ).join( X_String_CRLF ); + } else { + elm.data = X_String_chrReferanceTo( that[ '_text' ] ); + }; that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; return; }; + // id if( that[ '_flags' ] & X_NodeFlags_DIRTY_ID ){ - that[ '_id' ] ? ( elm.id = that[ '_id' ] ) : ( elm.id && elm.removeAttribute( 'id' ) ); + that[ '_id' ] ? + ( ( that[ '_flags' ] & X_NodeFlags_IS_SVG ) ? + elm.setAttribute( 'id', that[ '_id' ] ) : + ( elm.id = that[ '_id' ] ) + ) : + ( elm.id && elm.removeAttribute( 'id' ) ); }; // className if( that[ '_flags' ] & X_NodeFlags_DIRTY_CLASSNAME ){ - that[ '_className' ] ? ( elm.className = that[ '_className' ] ) : ( elm.className && elm.removeAttribute( X_UA[ 'IE' ] < 8 ? 'className' : 'class' ) ); // className は ie7- + that[ '_className' ] ? + ( ( that[ '_flags' ] & X_NodeFlags_IS_SVG ) ? + elm.setAttribute( 'class', that[ '_className' ] ) : + ( elm.className = that[ '_className' ] ) + ) : + ( elm.className && elm.removeAttribute( X_UA[ 'IE' ] < 8 ? 'className' : 'class' ) ); // className は ie7- }; // attr @@ -1636,11 +1878,27 @@ var X_Node__updateRawNode = // TODO IE では input, なぜか button, object も type, name の変更が出来ない、同値で置き換えようとしても不可 v === undefined ? elm.removeAttribute( rename[ k ] || k ) : - ( elm[ rename[ k ] || k ] = X_Node_Attr_noValue[ k ] ? k : v ); + ( ( that[ '_flags' ] & X_NodeFlags_IS_SVG ) ? + elm.setAttribute( k, v ) : + ( elm[ rename[ k ] || k ] = X_Node_Attr_noValue[ k ] ? k : v ) + ); }; delete that[ '_newAttrs' ]; }; + if( accumulatedFlags & X_NodeFlags_IE8_OPACITY_FIX ){ + if( 0 <= currentOpcity ){ + f = true; + that[ '_css' ].opacity = ie8AccumulatedOpcity; + if( that[ '_flags' ] & X_NodeFlags_DIRTY_CSS ){ + that[ '_flags' ] |= X_NodeFlags_OLD_CSSTEXT; + } else + if( !( that[ '_flags' ] & X_NodeFlags_DIRTY_IE_FILTER ) ){ + that[ '_flags' ] |= X_NodeFlags_DIRTY_IE_FILTER; + }; + }; + }; + // style if( that[ '_flags' ] & X_NodeFlags_DIRTY_CSS ){ if( that[ '_flags' ] & X_NodeFlags_OLD_CSSTEXT ? X_Node_CSS_objToCssText( that ) : that[ '_cssText' ] ){ @@ -1663,6 +1921,15 @@ var X_Node__updateRawNode = }; }; + /* + * http://jsdo.it/esukei/imOL + * IE8でのfilter:alpha継承 + * IE8でfilter:alphaの指定がposition:relative,position:absoluteの子要素に継承されない問題 + */ + if( f ){ + that[ '_css' ].opacity = currentOpcity; + }; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }) : X_UA_DOM.IE4 ? @@ -1837,7 +2104,7 @@ var X_Node__actualRemove = that[ '_listeners' ] && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 that[ '_flags' ] &= ~X_NodeFlags_ACTUAL_LISTENING; }; - + // ie5では filter の効いている要素をremove時に破棄して、再度append 時に新規生成する // ちなみに elm.filters に触ると ie8 でなぜかカラム落ちが発生、、、 if( X_Node_displayNoneFixForIE5 ){ @@ -1857,7 +2124,7 @@ var X_Node__actualRemove = if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; that[ '_attrs' ].selectedIndex = elm.selectedIndex; }; - if( that[ '_tag' ] === 'INPUT' && that._attr && ( that._attr.type === 'checkbox' || that._attr.type === 'radio' ) && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'checked', that[ '_newAttrs' ] ) ) ){ + if( that[ '_tag' ] === 'INPUT' && that[ '_attrs' ] && ( that[ '_attrs' ].type === 'checkbox' || that[ '_attrs' ].type === 'radio' ) && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'checked', that[ '_newAttrs' ] ) ) ){ if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; that[ '_attrs' ].checked = elm.checked; }; @@ -1900,7 +2167,7 @@ var X_Node__actualRemove = if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; that[ '_attrs' ].selectedIndex = elm.selectedIndex; }; - if( that[ '_tag' ] === 'INPUT' && that._attr && ( that._attr.type === 'checkbox' || that._attr.type === 'radio' ) && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'checked', that[ '_newAttrs' ] ) ) ){ + if( that[ '_tag' ] === 'INPUT' && that[ '_attrs' ] && ( that[ '_attrs' ].type === 'checkbox' || that[ '_attrs' ].type === 'radio' ) && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'checked', that[ '_newAttrs' ] ) ) ){ if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; that[ '_attrs' ].checked = elm.checked; };