X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F02_dom%2F02_XNode.js;h=d71a866b54b15c35502387ed486fc6d1f2c9cc8c;hb=6c4c72f7e862c9f950bfb3562adda24c39498abd;hp=1fa750c5e697e20100cd26dad22fe5ad3ac991cd;hpb=94c39d10a21853703c90cb09b1e82bd7a2d8923e;p=pettanr%2FclientJs.git diff --git a/0.6.x/js/02_dom/02_XNode.js b/0.6.x/js/02_dom/02_XNode.js index 1fa750c..d71a866 100644 --- a/0.6.x/js/02_dom/02_XNode.js +++ b/0.6.x/js/02_dom/02_XNode.js @@ -1,34 +1,74 @@ var - X_Node_Dirty = { - CLEAN : 0, - CHILD_IS_DIRTY : 1, - ID : 2, // width, height, x, y - CONTENT : 4, // width, height, x, y textNode の内容 - CLASSNAME : 8, // X_Node_CSS_getCharSize, width, height, x, y - ATTR : 16, // X_Node_CSS_getCharSize, width, height, x, y - CSS : 32, // X_Node_CSS_getCharSize, width, height, x, y - IE_FILTER : X_UA.IE < 9 && !X_UA.MacIE ? 64 : 0, - UNKNOWN_TAG_FIX : 128, - IE4_TEXTNODE_FIX : 256 - }, - + /** @enum {number} + * @const */ X_Node_State = { - DESTROYED : 0, - EXIST : 1, // XNODE_EXIDT, ELEMENT_EXIST - BELONG_TREE : 2, // HAS_PARENT, HAS_PARENT_ACTUAL, BELONG_TREE, BELONG_TREE_ACTUAL - DISPLAY_NONE : 4, // VISIVILITY_HIDDEN(opacity0), DISPALY_NONE - DISPLAY_BLOCK : 8, // remove - DISPLAY_INLINE : 16,// remove - POSITION_ABSOLUTE : 32, - OVERFLOW_HIDDEN : 64, - HAS_WIDTH_LENGTH : 128, - HAS_WIDTH_PERCENT : 256, - HAS_HEIGHT_LENGTH : 512, - HAS_HEIGHT_PERCENT : 1024, - IE4_ONLY_TEXT : 2048, - IE5_DISPLAY_NONE_FIX : !X_UA.MacIE && X_UA.IE5 ? 4096 : 0 // filterがかかっていると不可? MacIE5.2- は ? - }, + DESTROYED : 0x0, + EXIST : 0x1, + IN_TREE : 0x2, // xnode が(仮想)ツリーに追加されている -> 描画の対象 + + STYLE_IS_DISPLAY_NONE : 2 << 1, // display : none + STYLE_IS_INVISIBLE : 2 << 2, // visibility : hidden or opacity : 0 + STYLE_IS_POS_ABSOLUTE : 2 << 3, // position : absolute + STYLE_IS_NO_OVERFLOW : 2 << 4, // overflow : hidden + STYLE_IS_WIDTH_LENGTH : 2 << 5, // width : overflow:hidden,要素無し、または要素が非表示なら、 width() のための commitUpdate が不要 + STYLE_IS_WIDTH_PCT : 2 << 6, // width : width() のための commitUpdate が不要かもしれない。(親で LENGTH が指定されているなら) + STYLE_IS_HEIGHT_LENGTH : 2 << 7, // height : + STYLE_IS_HEIGHT_PCT : 2 << 8, // height : + STYLE_IS_FONT_LENGTH : 2 << 9, // fontSize : + STYLE_IS_FONT_PCT : 2 << 10, // fontSize : + + DIRTY_POSITION : 2 << 11, // 要素位置の変更が起こった。 + DIRTY_CONTENT : 2 << 12, // width, height, x, y textNode の内容 TODO html と排他なので ID と共通でいい + DIRTY_ID : 2 << 12, // width, height, x, y + DIRTY_CLASSNAME : 2 << 13, // X_Node_CSS_getCharSize, width, height, x, y + DIRTY_ATTR : 2 << 14, // X_Node_CSS_getCharSize, width, height, x, y + DIRTY_CSS : 2 << 15, // X_Node_CSS_getCharSize, width, height, x, y + DIRTY_IE_FILTER : X_UA[ 'IE' ] < 10 && X_UA[ 'ActiveX' ] ? 2 << 16 : 0, // + + ACTUAL_LISTENING : 2 << 17, + OLD_ATTRTEXT : 2 << 18, + OLD_CSSTEXT : 2 << 19, + + // filter 要素が親子になると不具合が出るのを検出 + IE_FILTER_NOW : 2 << 20, + + //GPU_WAITING : 2 << 20, // 1:子のGPU解除待 + GPU_RESERVED : 2 << 21, // 2:GPU予約 + GPU_NOW : 2 << 22, // 3:GPU now! + GPU_RELEASE_RESERVED : 2 << 23, // 4:GPU解除予約 + GPU_CHILD : 2 << 24, + + IE4_HAS_TEXTNODE : X_UA[ 'IE4' ] ? 2 << 21 : 0, + IE4_HAS_ELEMENT : X_UA[ 'IE4' ] ? 2 << 22 : 0, + IE4_DIRTY_CHILDREN : X_UA[ 'IE4' ] ? 2 << 23 : 0, + IE4_FIXED : X_UA[ 'IE4' ] ? 2 << 24 : 0, + + IE5_DISPLAY_NONE_FIX : X_UA[ 'IE5' ] && X_UA[ 'ActiveX' ] ? 2 << 24 : 0 + }; + +var X_Node_BITMASK_RESET_STYLE = ( ( 2 << 29 ) - 1 + ( 2 << 29 ) ) ^ ( + X_Node_State.STYLE_IS_DISPLAY_NONE | + X_Node_State.STYLE_IS_INVISIBLE | + X_Node_State.STYLE_IS_POS_ABSOLUTE | + X_Node_State.STYLE_IS_NO_OVERFLOW | + X_Node_State.STYLE_IS_WIDTH_LENGTH | + X_Node_State.STYLE_IS_WIDTH_PCT | + X_Node_State.STYLE_IS_HEIGHT_LENGTH | + X_Node_State.STYLE_IS_HEIGHT_PCT | + X_Node_State.STYLE_IS_FONT_LENGTH | + X_Node_State.STYLE_IS_FONT_PCT ), + + X_Node_BitMask_IS_DIRTY = X_Node_State.DIRTY_POSITION | + X_Node_State.DIRTY_CONTENT | X_Node_State.DIRTY_ID | X_Node_State.DIRTY_CLASSNAME | + X_Node_State.DIRTY_ATTR | X_Node_State.DIRTY_CSS | X_Node_State.DIRTY_IE_FILTER, + + X_Node_BitMask_RESET_DIRTY = ( ( 2 << 29 ) - 1 + ( 2 << 29 ) ) ^ X_Node_BitMask_IS_DIRTY, + + X_Node_BitMask_RESET_GPU = ( ( 2 << 29 ) - 1 + ( 2 << 29 ) ) ^ ( X_Node_State.GPU_RESERVED | X_Node_State.GPU_NOW | X_Node_State.GPU_RELEASE_RESERVED ), + X_Node_BitMask_IE4_IS_MIX = X_Node_State.IE4_HAS_TEXTNODE | X_Node_State.IE4_HAS_ELEMENT, + + /* @enum {number} */ X_Node_TYPE = { XNODE : 1, RAW_HTML : 2, @@ -42,9 +82,14 @@ var IMAGE : 10 }, - X_Node_strictElmCreation = !X_UA.MacIE && X_UA.IE5678,// && !X_UA.MacIE; + X_Node_strictElmCreation = !X_UA[ 'MacIE' ] && X_UA[ 'IE' ] <= 8, + + X_Node_documentFragment = document.createDocumentFragment && ( !X_UA[ 'IE' ] || 5.5 <= X_UA[ 'IE' ] ) && document.createDocumentFragment(), - X_Node_useDocumentFragment = document.createDocumentFragment && ( !X_UA.IE || 5.5 <= X_UA.IE ) && document.createDocumentFragment(), + // 子の生成後に リアル文書 tree に追加する + X_Node_addTreeAfterChildren = !( X_UA[ 'IE' ] < 9 ), + + X_Node_displayNoneFixForIE5 = !!X_Node_State.IE5_DISPLAY_NONE_FIX, X_Node_newByTag = false, @@ -52,184 +97,288 @@ var X_Node_outerXNode = null, - X_Node_updateTimerID = 0, -/* + X_Node_updateTimerID = 0; + +/** * Node( rawElement | rawTextnode | htmlString | textString ) - */ - Node = X.Node = X.EventDispatcher.inherits( - 'XDomNode', - X.Class.POOL_OBJECT, + * + * @alias X.Node + * @class Node HTMLElement、TextNode をラップし jQuery 風な API で操作できます。 + * @constructs Node + * @extends {EventDispatcher} + */ +var Node = X[ 'Node' ] = X_EventDispatcher[ 'inherits' ]( + 'X.Node', + X_Class.POOL_OBJECT, + { - _uid : 0, - _state : 0, - _dirty : 0, + /** + * 要素に振られるユニークID + * @type {number} + * @private + * @alias Node.prototype._uid + */ + '_uid' : 0, - _isNew : false, // state にまとめる + /** + * Node の状態を表すフラグ。 + * @type {number} + * @private + * @alias Node.prototype._flags + */ + '_flags' : X_Node_State.DESTROYED, + + // _rawObject : null, + '_rect' : null, // - _rawObject : null, - _rect : null, // + /** + * 最後に計測したフォントサイズを保持している。ツリーが変更されると削除される。 + * @type {number} + * @private + * @alias Node.prototype._fontSize + */ + '_fontSize' : 0, - _root : null, // xnode が文書ツリーに属しているか?はこれを見る -> state - parent : null, // remove された枝も親子構造は維持している。 - _xnodes : null, - - _xnodeType : 0, - _tag : null, - _text : null, - _id : null, - _className : '', - - _attrs : null, // see X_Node_Attr - _newAttrs : null, - _attrText : '', // X_Node_Attr_objToAttrText が必要な場合は false が入っている + /** + * NodeList と動作を一致させるためのプロパティ。常に 1。 + * @type {number} + * @alias Node.prototype.length + */ + length : 1, + + /** + * 親 Node。 + * @type {Node} + * @alias Node.prototype.parent + */ + parent : null, // remove された枝も親子構造は維持している。 + + /** + * 子 Node リスト + * @type {Array} + * @private + * @alias Node.prototype._xnodes + */ + '_xnodes' : null, + + /** + * GPU レイヤーに転送されている場合、その一番親となっている Node。未実装。 + * @type {Node} + * @private + * @alias Node.prototype._gpuParent + */ + '_gpuParent' : null, + + /** + * タグ名。テキストノードの場合は空文字列。 + * @type {string} + * @private + * @alias Node.prototype._tag + */ + '_tag' : '', - _css : null, // see X_Node_CSS - _cssText : null, + /** + * テキストコンテンツ。テキストノードで使用。 + * @type {string} + * @private + * @alias Node.prototype._text + */ + '_text' : '', - _fontSize : 0, + /** + * id + * @type {string} + * @private + * @alias Node.prototype._id + */ + '_id' : '', - _anime : null, + /** + * クラス名。複数のクラスが設定されている場合、スペース区切り。 + * @type {string} + * @private + * @alias Node.prototype._className + */ + '_className' : '', // + + /** + * 属性。 + * @type {object} + * @private + * @alias Node.prototype._attrs + */ + '_attrs' : null, // see X_Node_Attr + + /** + * まだコミットされていない属性。 + * @type {object} + * @private + * @alias Node.prototype._newAttrs + */ + '_newAttrs' : null, + + /** + * 属性を文字列にしたもの。 color="red" size="8" + * @type {object} + * @private + * @alias Node.prototype._attrText + */ + '_attrText' : '', + + /** + * スタイル。 + * @type {object} + * @private + * @alias Node.prototype._css + */ + '_css' : null, + + /** + * cssText + * @type {string} + * @private + * @alias Node.prototype._cssText + */ + '_cssText' : '', + + /** + * アニメーション用オブジェクト。 + * @type {object} + * @private + * @alias Node.prototype._anime + */ + '_anime' : null, /* * TODO Node の継承ができない! */ - Constructor : function( v ){ - var css, xnodes, xnode, parent, uid = X_Node_CHASHE.length; + 'Constructor' : function( v ){ + var uid = X_Node_CHASHE.length, + css, xnodes, xnode, parent; if( X_Node_newByTag ){ X_Node_newByTag = false; - this._tag = v.toUpperCase(); - this._xnodeType = 1; - this._state = X_Node_State.DISPLAY_INLINE; // TODO - arguments[ 1 ] && this.attr( arguments[ 1 ] ); + this[ '_tag' ] = v.toUpperCase(); + arguments[ 1 ] && this[ 'attr' ]( arguments[ 1 ] ); css = arguments[ 2 ]; - css && this[ X.Type.isString( css ) ? 'cssText' : 'css' ]( css ); + css && this[ X_Type_isString( css ) ? 'cssText' : 'css' ]( css ); } else if( X_Node_newByText ){ X_Node_newByText = false; - this._text = v; - this._xnodeType = 3; - this._state = X_Node_State.DISPLAY_INLINE; + this[ '_text' ] = v; } else { if( 1 < arguments.length ) return new X_NodeList( arguments ); - if( X.Type.isArray( v ) && v.length ) return new X_NodeList( v ); + if( X_Type_isArray( v ) && v.length ) return new X_NodeList( v ); switch( X_Node_getType( v ) ){ case X_Node_TYPE.XNODE : case X_Node_TYPE.XNODE_LIST : return v; + case X_Node_TYPE.RAW_HTML : if( xnode = X_Node_getXNode( v ) ) return xnode; // v.parentNode || v.parentElement : dom1 || dom0 this.parent = ( parent = v.parentNode || v.parentElement ) && parent.tagName /* ie7- */ && X_Node_getXNode( parent ); - if( this.parent && this.parent._root ) this._root = this.parent._root; - this._rawObject = v; - this._xnodeType = 1; - this._state = X_Node_State.DISPLAY_BLOCK; // TODO - this._tag = v.tagName.toUpperCase(); - this._id = v.id; - this._className = v.className; - this.cssText( v.style.cssText ); - // X_Node_Dirty.CSS を落とす - this._dirty = 0; - // TODO attr の回収は不可能、、、 + this[ '_rawObject' ] = v; + this[ '_tag' ] = v.tagName.toUpperCase(); + this[ '_id' ] = v.id; + this[ '_className' ] = v.className; + + this[ 'cssText' ]( v.style.cssText ); + this[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; // X_Node_State.DIRTY_CSS を落とす + + // TODO attr の回収は不可能、、、? if( X_UA_DOM.IE4 ){ v.setAttribute( 'UID', '' + uid ); } else { - v.UID = uid; + v[ 'UID' ] = uid; }; // childNodes... break; + case X_Node_TYPE.RAW_TEXT : if( xnode = X_Node_getXNode( v ) ) return xnode; this.parent = X_Node_getXNode( v.parentNode ); - if( this.parent && this.parent._root ) this._root = this.parent._root; - this._rawObject = v; - this._xnodeType = 3; - this._state = X_Node_State.DISPLAY_INLINE; - this._text = v.data; - v.UID = uid; + this[ '_rawObject' ] = v; + this[ '_text' ] = v.data; + v[ 'UID' ] = uid; break; + case X_Node_TYPE.HTML_STRING : case X_Node_TYPE.STRING : if( xnodes = X_HtmlParser_parse( v, true ) && 1 < xnodes.length ) return new X_NodeList( xnodes ); if( xnodes.length ) return xnodes[ 0 ]; return X_Node_none; - /* - case X_Node_TYPE.IMAGE : - if( xnode = X_Node_getXNode( v ) ) return xnode; - this._rawObject = v; - this._xnodeType = 4; - v.UID = uid; - this._state = X_Node_State.EXIST; - break; */ + default : if( X_Node_none ) return X_Node_none; + this.length = 0; return; }; }; - X_Node_CHASHE[ this._uid = uid ] = this; + if( this.parent && ( this.parent[ '_flags' ] & X_Node_State.IN_TREE ) ){ + this[ '_flags' ] |= X_Node_State.IN_TREE; + }; + this[ '_flags' ] |= X_Node_State.EXIST; + X_Node_CHASHE[ this[ '_uid' ] = uid ] = this; + + X_EventDispatcher_systemListen( this, X_EVENT_BEFORE_KILL_INSTANCE, X_Node_onBeforeKill ); }, - create : X_Node_create, - - createAt : X_Node_createAt, - - createText : X_Node_createText, - - createTextAt : X_Node_createTextAt, - - clone : X_Node_clone, + // attr + // css, cssText + // find + // animate, stop - append : X_Node_append, + 'create' : X_Node_create, - appendAt : X_Node_appendAt, + 'createAt' : X_Node_createAt, - appendTo : X_Node_appendTo, + 'createText' : X_Node_createText, - appendToRoot : X_Node_appendToRoot, + 'createTextAt' : X_Node_createTextAt, - before : X_Node_before, // remove + 'clone' : X_Node_clone, - prevNode : X_Node_before, // -> prev + 'append' : X_Node_append, - after : X_Node_after, // remove + 'appendAt' : X_Node_appendAt, - nextNode : X_Node_after, // -> next + 'appendTo' : X_Node_appendTo, - replace : X_Node_replace, // remove + 'prev' : X_Node_prev, - swap : X_Node_replace, + 'next' : X_Node_next, - remove : X_Node_remove, + 'swap' : X_Node_swap, - empty : X_Node_empty, + 'remove' : X_Node_remove, - destroy : X_Node_destroy, // -> kill && kill event + 'empty' : X_Node_empty, - contains : X_Node_contains, + 'contains' : X_Node_contains, - getChildAt : X_Node_getChildAt, + 'getChildAt' : X_Node_getChildAt, - numChildren : X_Node_length, + 'numChildren' : X_Node_numChildren, - firstChild : X_Node_firstChild, + 'firstChild' : X_Node_firstChild, - lastChild : X_Node_lastChild, + 'lastChild' : X_Node_lastChild, - getOrder : X_Node_getOrder, + 'getOrder' : X_Node_getOrder, - className : X_Node_className, - addClass : X_Node_addClass, - removeClass : X_Node_removeClass, - toggleClass : X_Node_toggleClass, - hasClass : X_Node_hasClass, + 'className' : X_Node_className, + 'addClass' : X_Node_addClass, + 'removeClass' : X_Node_removeClass, + 'toggleClass' : X_Node_toggleClass, + 'hasClass' : X_Node_hasClass, - html : X_Node_html, - text : X_Node_text, - call : X_Node_call, - each : X_Node_each + 'html' : X_Node_html, + 'text' : X_Node_text, + 'call' : X_Node_call, + 'each' : X_Node_each } ); @@ -241,13 +390,12 @@ function X_Node_getType( v ){ if( v === document ) return X_Node_TYPE.DOCUMENT; if( v.constructor === Node ) return X_Node_TYPE.XNODE; if( v.constructor === X_NodeList ) return X_Node_TYPE.XNODE_LIST; - if( X.Type.isHTMLElement( v ) ) return X_Node_TYPE.RAW_HTML; + if( X_Type_isHTMLElement( v ) ) return X_Node_TYPE.RAW_HTML; if( v.nodeType === 3 ) return X_Node_TYPE.RAW_TEXT; - //if( X.Type.isImage( v ) ) return X_Node_TYPE.IMAGE; - if( X.Type.isString( v ) ){ + if( X_Type_isString( v ) ){ return '<' === v.charAt( 0 ) && v.charAt( v.length - 1 ) === '>' ? X_Node_TYPE.HTML_STRING : X_Node_TYPE.STRING; }; - //if( v.nodeType === 11 ) return X_Node_TYPE.DOC_FRAG; + if( v[ 'instanceOf' ] && v[ 'instanceOf' ]( Node ) ) return X_Node_TYPE.XNODE; return 0; }; function X_Node_getXNode( v ){ @@ -257,171 +405,210 @@ function X_Node_getXNode( v ){ case X_Node_TYPE.XNODE_LIST : return v; case X_Node_TYPE.RAW_HTML : - //case X_Node_TYPE.IMAGE : // fake TextNode too. if( X_UA_DOM.IE4 ){ uid = v.getAttribute( 'UID' ); return uid && X_Node_CHASHE[ uid ]; }; - return v.UID && X_Node_CHASHE[ v.UID ]; + return v[ 'UID' ] && X_Node_CHASHE[ v[ 'UID' ] ]; case X_Node_TYPE.WINDOW : return X_ViewPort; case X_Node_TYPE.DOCUMENT : return X_ViewPort_document; case X_Node_TYPE.RAW_TEXT : - if( v.UID ) return X_Node_CHASHE[ v.UID ]; + 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 = X_Node_CHASHE[ --i ] ) && ( xnode[ '_rawObject' ] === v ) ) return xnode; }; }; }; function X_Node_getRoot( xnode ){ return X_ViewPort_document; - //return X_Node_body._rawObject.documentElement ? node : node.ownerDocument || node.document; + //return X_Node_body[ '_rawObject' ].documentElement ? node : node.ownerDocument || node.document; }; // XMLかどうかを判別する var X_Node_isXmlDocument = X_UA_DOM.IE4 ? - X.emptyFunction : + X_emptyFunction : (function( root ){ - if( X.Type.isBoolean( root.isXML ) ) return root.isXML; - return root.isXML = root._rawObject.createElement( 'p' ).tagName !== root._rawObject.createElement( 'P' ).tagName; + if( X_Type_isBoolean( root.isXML ) ) return root.isXML; + return root.isXML = root[ '_rawObject' ].createElement( 'p' ).tagName !== root[ '_rawObject' ].createElement( 'P' ).tagName; }), X_Node_CHASHE = [], - X_Node_none = X_Node_CHASHE[ 0 ] = new Node(), + X_Node_none = X_Node_CHASHE[ 0 ] = Node(), X_Node_html, // = X_Node_CHASHE[ 1 ] X_Node_head, // = X_Node_CHASHE[ 2 ] X_Node_body, // = X_Node_CHASHE[ 3 ] X_Node_systemNode, // = X_Node_CHASHE[ ? ] X_Node_fontSizeNode, +/* + * remove : + * X_Node_reserveRemoval = [] に追加。commitUpdate で remove + * add : + * X_Node_reserveRemoval にいたら消す, new_parent[ '_xnodes' ] に挿入 + */ X_Node_reserveRemoval = []; var X_Node__ie4getRawNode = X_UA_DOM.IE4 && function ( that ){ - var elm = that._rawObject; - return elm || - ( ( elm = document.all[ 'ie4uid' + that._uid ] ) && ( that._rawObject = elm ) ) || - ( that._id && ( elm = document.all[ that._id ] ) ) && ( that._rawObject = elm ); + return that[ '_rawObject' ] || + ( that[ '_rawObject' ] = document.all[ 'ie4uid' + that[ '_uid' ] ] ) || + ( that[ '_id' ] && ( that[ '_rawObject' ] = document.all[ that[ '_id' ] ] ) ); }; -/* -------------------------------------- - * Create +function X_Node_toggleInTreeFlag( xnodes, flag ){ + var i = xnodes.length, xnode; + for( ; i; ){ + xnode = xnodes[ --i ]; + flag ? ( xnode[ '_flags' ] |= X_Node_State.IN_TREE | X_Node_State.DIRTY_POSITION ) : ( xnode[ '_flags' ] &= ~X_Node_State.IN_TREE & ~X_Node_State.IE5_DISPLAY_NONE_FIX ); + xnode[ '_xnodes' ] && X_Node_toggleInTreeFlag( xnode[ '_xnodes' ], flag ); + }; +}; + +function X_Node_toggleInGPUFlag( gpuRoot, xnodes, flag ){ + var i = xnodes.length, xnode; + + if( flag ){ + for( ; i; ){ + xnode = xnodes[ --i ]; + if( !xnode[ '_gpuParent' ] ){ + xnode[ '_flags' ] |= X_Node_State.GPU_CHILD; + xnode[ '_gpuParent' ] = gpuRoot; + xnode[ '_xnodes' ] && X_Node_toggleInTreeFlag( gpuRoot, xnode[ '_xnodes' ], flag ); + }; + }; + } else { + for( ; i; ){ + xnode = xnodes[ --i ]; + if( xnode[ '_gpuParent' ] === gpuRoot ){ + xnode[ '_flags' ] &= ~X_Node_State.GPU_CHILD; + delete xnode[ '_gpuParent' ]; + xnode[ '_xnodes' ] && X_Node_toggleInTreeFlag( gpuRoot, xnode[ '_xnodes' ], flag ); + }; + }; + }; +}; + +/** + * タグ名等を指定して新規に子ノードを作成し、現在のノードに追加する。 + * @alias Node.prototype.create + * @param {string} [tag] タグ名 + * @param {object} [opt_attrs=] 属性 + * @param {object|string} [opt_css=] css + * @return {Node} 新規作成されたノード + * @example var child = parent.create( 'div' ); */ function X_Node_create( tag, opt_attrs, opt_css ){ var xnode; - if( this._xnodeType !== 1 ) return; - if( !this._xnodes ) this._xnodes = []; - - xnode = X_Doc_create( tag, opt_attrs, opt_css ); - - xnode.parent = this; - this._xnodes[ this._xnodes.length ] = xnode; - this._root && X_Node_reserveUpdate(); + if( !this[ '_tag' ] ) return; + this[ 'append' ]( xnode = X_Doc_create( tag, opt_attrs, opt_css ) ); return xnode; }; +/** + * 挿入位置とタグ名等を指定して新規に子ノードを作成し、現在のノードに挿入する。 + * @alias Node.prototype.createAt + * @param {number} [index] 挿入位置 + * @param {string} [tag] タグ名 + * @param {object} [opt_attrs=] 属性 + * @param {object|string} [opt_css=] css + * @return {Node} 新規作成されたノード + * @example var child = parent.create( 2, 'div' ); + */ function X_Node_createAt( index, tag, opt_attrs, opt_css ){ - var xnode = X_Doc_create( tag, opt_attrs, opt_css ); - this.appendAt( index, xnode ); + var xnode; + if( !this[ '_tag' ] ) return; + this[ 'appendAt' ]( index, xnode = X_Doc_create( tag, opt_attrs, opt_css ) ); return xnode; }; -/* -------------------------------------- - * CreateText +/** + * テキストを指定して新規にテキストノードを作成し、現在のノードに挿入する。 + * @alias Node.prototype.createText + * @param {string} [tag] テキスト + * @return {Node} 新規作成されたノード */ function X_Node_createText( text ){ var xnode; - if( this._xnodeType !== 1 ) return; - if( !this._xnodes ) this._xnodes = []; - - X_Node_newByText = true; - xnode = new Node( text ); - xnode.parent = this; - - this._root && X_Node_reserveUpdate(); - this._xnodes[ this._xnodes.length ] = xnode; + if( !this[ '_tag' ] ) return; + this[ 'append' ]( xnode = X_Doc_createText( text ) ); return xnode; }; +/** + * 挿入位置とテキストを指定して新規に子ノードを作成し、現在のノードに挿入する。 + * @alias Node.prototype.createTextAt + * @param {number} [index] 挿入位置 + * @param {string} [tag] テキスト + * @return {Node} 新規作成されたノード + */ function X_Node_createTextAt( index, text ){ - var xtext = X_Doc_createText( text ); - this.appendAt( index, xtext ); - return xtext; + var xnode; + if( !this[ '_tag' ] ) return; + this[ 'appendAt' ]( index, xnode = X_Doc_createText( text ) ); + return xnode; }; -/* -------------------------------------- - * Clone +/** + * Node のクローンを作成し返す。id もクローンされる点に注意。イベントリスナはクローンされない。 * http://d.hatena.ne.jp/think49/20110724/1311472811 * http://d.hatena.ne.jp/uupaa/20100508/1273299874 + * @alias Node.prototype.clone + * @param {boolean} [opt_clone_children] 子要素のクローンを行うか? + * @return {Node} */ function X_Node_clone( opt_clone_children ){ var xnode, xnodes, i, l; - switch( this._xnodeType ){ - case 1 : - X_Node_newByTag = true; - xnode = new Node( this._tag, X_Object_clone( this._attrs ), X_Object_clone( this._css ) ) - .attr( { 'id' : this._id } ) - .className( this._className ); - if( opt_clone_children && ( xnodes = this._xnodes ) && ( l = xnodes.length ) ){ - for( i = 0; i < l; ++i ){ - xnode.append( xnodes[ i ].clone( true ) ); - }; + if( this[ '_tag' ] ){ + X_Node_newByTag = true; + xnode = Node( this[ '_tag' ], X_Object_clone( this[ '_attrs' ] ), X_Object_clone( this[ '_css' ] ) ) + [ 'attr' ]( { 'id' : this[ '_id' ] } ) + [ 'className' ]( this[ '_className' ] ); + if( opt_clone_children && ( xnodes = this[ '_xnodes' ] ) && ( l = xnodes.length ) ){ + for( i = 0; i < l; ++i ){ + xnode[ 'append' ]( xnodes[ i ][ 'clone' ]( true ) ); }; - return xnode; - case 3 : - X_Node_newByText = true; - xnode = new Node( this._text ); - return xnode; - - //case 0 : - //case 2 : + }; + return xnode; }; - return this; + X_Node_newByText = true; + return Node( this[ '_text' ] ); }; -/* -------------------------------------- - * Add - * Node - * HtmlElement の場合は内部使用専用 そのため event の破棄等しない +/** + * ノードを子配列の最後に追加する。文字列が渡された場合、HTMLパーサーによって Node ツリーを作成して追加する。HtmlElement, TextNode の場合は内部使用専用。 + * @alias Node.prototype.append + * @param {Node|string|HTMLElement|TextNode} [v] HTMLElement と TextNode は内部のみ。 + * @return {Node} 自身。チェインメソッド + * @example myNode.append( node ); + * myNode.append( nodeSprite, '
A
', 'B' ); */ function X_Node_append( v ){ var i, l, xnodes, frg; - if( this._xnodeType !== 1 ) return; + if( !this[ '_tag' ] ) return; if( 1 < ( l = arguments.length ) ){ for( i = 0; i < l; ++i ){ - this.append( arguments[ i ] ); + this[ 'append' ]( arguments[ i ] ); }; return this; }; - if( !( xnodes = this._xnodes ) ) this._xnodes = xnodes = []; + if( !( xnodes = this[ '_xnodes' ] ) ) this[ '_xnodes' ] = xnodes = []; switch( X_Node_getType( v ) ){ case X_Node_TYPE.RAW_HTML : case X_Node_TYPE.RAW_TEXT : - v = new Node( v ); + v = Node( v ); break; case X_Node_TYPE.HTML_STRING : case X_Node_TYPE.STRING : - return this.append.apply( this, X_HtmlParser_parse( v, true ) ); + return X_Node_append.apply( this, X_HtmlParser_parse( v, true ) ); case X_Node_TYPE.XNODE : - if( v._xnodeType !== 1 && v._xnodeType !== 3 ) return this; // 親の xnodes から v を消す - if( v.parent ){ - //if( X_UA_DOM.W3C ){ - // v.parent._xnodes.splice( v.parent._xnodes.indexOf( v ), 1 ); - //} else - //if( X_UA_DOM.IE4 ){ - v.remove(); - //} else { - - //}; - };// else - //if( ( i = X_Node_reserveRemoval.indexOf( v ) ) !== -1 ){ - // if( !this._state ) alert( 'xnode already destroyed!' ); - // X_Node_reserveRemoval.splice( i, 1 ); - //}; + v.parent && v[ 'remove' ](); + // IE4 でテキストノードの追加、FIXED 済でない場合、親に要素の追加を通知 + if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( this[ '_flags' ] & X_Node_State.IE4_FIXED ) === 0 ) this[ '_flags' ] |= X_Node_State.IE4_DIRTY_CHILDREN; break; default : return this; @@ -429,30 +616,41 @@ function X_Node_append( v ){ v.parent = this; xnodes[ xnodes.length ] = v; - this._root && X_Node_reserveUpdate(); + if( this[ '_flags' ] & X_Node_State.IN_TREE ){ + v[ '_flags' ] |= X_Node_State.IN_TREE; + v[ '_xnodes' ] && X_Node_toggleInTreeFlag( v[ '_xnodes' ], true ); + X_Node_reserveUpdate(); + }; return this; }; - +/** + * ノードを挿入位置に追加する。 + * @alias Node.prototype.appendAt + * @param {number} index 挿入位置 0以上 + * @param {Node|string|HTMLElement|TextNode} [v] HTMLElement と TextNode は内部のみ。 + * @return {Node} 自身。チェインメソッド + * @example myNode.appendAt( 1, node ); + */ function X_Node_appendAt( start, v ){ var xnodes, l, i; - if( this._xnodeType !== 1 ) return this; + if( !this[ '_tag' ] ) return this; l = arguments.length; - if( !( xnodes = this._xnodes ) ) xnodes = this._xnodes = []; + if( !( xnodes = this[ '_xnodes' ] ) ) xnodes = this[ '_xnodes' ] = []; if( xnodes.length <= start ){ - if( l === 2 ) return this.append( v ); + if( l === 2 ) return this[ 'append' ]( v ); for( i = 1; i < l; ++i ){ - this.append( arguments[ i ] ); + this[ 'append' ]( arguments[ i ] ); }; return this; }; if( start < 0 ) start = 0; if( 2 < l ){ for( ; l; ){ - this.appendAt( start, arguments[ --l ] ); + this[ 'appendAt' ]( start, arguments[ --l ] ); }; return this; }; @@ -460,47 +658,54 @@ function X_Node_appendAt( start, v ){ switch( X_Node_getType( v ) ){ case X_Node_TYPE.RAW_HTML : case X_Node_TYPE.RAW_TEXT : - v = new Node( v ); + v = Node( v ); break; case X_Node_TYPE.HTML_STRING : case X_Node_TYPE.STRING : v = X_HtmlParser_parse( v, true ); for( i = v.length; i; ){ - this.appendAt( start, v[ --i ] ); + this[ 'appendAt' ]( start, v[ --i ] ); }; return this; case X_Node_TYPE.XNODE : - if( v._xnodeType !== 1 && v._xnodeType !== 3 ) return this; // 親の xnodes から v を消す if( v.parent ){ - //if( X_UA_DOM.W3C ){ - // v.parent._xnodes.splice( v.parent._xnodes.indexOf( v ), 1 ); - //} else - //if( X_UA_DOM.IE4 ){ - v.remove(); - //} else { - - //}; - };// else - //if( ( i = X_Node_reserveRemoval.indexOf( v ) ) !== -1 ){ - // if( !this._state ) alert( 'xnode already destroyed!' ); - // X_Node_reserveRemoval.splice( i, 1 ); - //}; + if( v.parent === this ){ + i = v[ 'getOrder' ](); + if( i === start ) return this; + if( i < start ) --start; + }; + v[ 'remove' ](); + }; + // IE4 でテキストノードの追加、FIXED 済でない場合、親に要素の追加を通知 + if( X_UA[ 'IE4' ] && !v[ '_tag' ] && ( this[ '_flags' ] & X_Node_State.IE4_FIXED ) === 0 ) this[ '_flags' ] |= X_Node_State.IE4_DIRTY_CHILDREN; break; default : return this; }; v.parent = this; - this._xnodes.splice( start, 0, v ); - this._root && X_Node_reserveUpdate(); + this[ '_xnodes' ].splice( start, 0, v ); + if( this[ '_flags' ] & X_Node_State.IN_TREE ){ + v[ '_flags' ] |= X_Node_State.IN_TREE; + v[ '_xnodes' ] && X_Node_toggleInTreeFlag( v[ '_xnodes' ], true ); + X_Node_reserveUpdate(); + }; return this; }; +/** + * ノードを親に追加する。戻り値は子ノードなので、続けて操作が出来る。 + * @alias Node.prototype.appendTo + * @param {Node|string|HTMLElement} [parent] HTMLElement は内部のみ。 + * @param {number} [opt_index=-1] 挿入位置。省略した場合は最後に追加する。 + * @return {Node} 自身。チェインメソッド + * @example childNode.appendTo( parentNode, 1 ); + */ function X_Node_appendTo( parent, opt_index ){ switch( X_Node_getType( parent ) ){ case X_Node_TYPE.RAW_HTML : - parent = new Node( parent ); + parent = Node( parent ); break; case X_Node_TYPE.HTML_STRING : parent = X_HtmlParser_parse( parent, true ); @@ -510,25 +715,25 @@ function X_Node_appendTo( parent, opt_index ){ default : return this; }; - opt_index === undefined ? parent.append( this ) : parent.appendAt( opt_index, this ); + X_Type_isFinite( opt_index ) ? parent[ 'appendAt' ]( opt_index, this ) : parent[ 'append' ]( this ); return this; }; -function X_Node_appendToRoot( opt_index ){ - opt_index === undefined ? X_Node_body.append( this ) : X_Node_body.appendAt( opt_index, this ); - return this; -}; -/* -------------------------------------- - * Before , After, Replace +/** + * ノードの直前の要素を取得。または直前に挿入。挿入する要素が先にいる兄弟でも正しく動作する。 + * @alias Node.prototype.prev + * @param {Node|string|HTMLElement|TextNode} [...v] HTMLElement と TextNode は内部のみ。 + * @return {Node} 自身。チェインメソッド + * @example childNode.prev( prevNode ); */ -function X_Node_before( v ){ - var parent = this.parent, xnodes, i, l, start; +function X_Node_prev( v ){ + var parent = this.parent, xnodes, i, l; // getter if( v === undefined ){ if( !parent ) return; - xnodes = parent._xnodes; + xnodes = parent[ '_xnodes' ]; i = xnodes.indexOf( this ); return 0 < i ? xnodes[ i - 1 ] : v; }; @@ -536,24 +741,30 @@ function X_Node_before( v ){ if( !parent ) return this; l = arguments.length; - start = this.getOrder(); if( 1 < l ){ - for( ; l; ){ - parent.appendAt( start, arguments[ --l ] ); + for( i = 0; l; ++i ){ + parent[ 'appendAt' ]( this[ 'getOrder' ]() - i, arguments[ --l ] ); }; return this; }; - parent.appendAt( start, v ); + parent[ 'appendAt' ]( this[ 'getOrder' ](), v ); return this; }; -function X_Node_after( v ){ +/** + * ノードの直後の要素を取得。または直後に挿入。挿入する要素が先にいる兄弟でも正しく動作する。 + * @alias Node.prototype.next + * @param {Node|string|HTMLElement|TextNode} [v] HTMLElement と TextNode は内部のみ。 + * @return {Node} 自身。チェインメソッド + * @example childNode.next( prevNode ); + */ +function X_Node_next( v ){ var parent = this.parent, xnodes, i, l, start; // getter if( v === undefined ){ if( !parent ) return; - xnodes = parent._xnodes; + xnodes = parent[ '_xnodes' ]; i = xnodes.indexOf( this ); return ++i < xnodes.length ? xnodes[ i ] : v; }; @@ -561,161 +772,222 @@ function X_Node_after( v ){ if( !parent ) return this; l = arguments.length; - start = this.getOrder() + 1; - if( parent._xnodes.length <= start ){ - if( 1 < l ){ - for( i = 0; i < l; ++i ){ - parent.append( arguments[ i ] ); - }; - return this; + start = this[ 'getOrder' ]() + 1; + + if( parent[ '_xnodes' ].length <= start ){ + for( i = 0; i < l; ++i ){ + parent[ 'append' ]( arguments[ i ] ); }; - parent.append( v ); - return this; - }; + } else if( 1 < l ){ for( ; l; ){ - parent.appendAt( start, arguments[ --l ] ); + parent[ 'appendAt' ]( this[ 'getOrder' ]() + 1, arguments[ --l ] ); }; - return this; + } else { + parent[ 'appendAt' ]( start, v ); }; - parent.appendAt( start, v ); return this; }; -function X_Node_replace( v ){ +/** + * 要素の入れ替え。自身は remove() される。 + * @alias Node.prototype.swap + * @param {Node|string|HTMLElement|TextNode} [v] HTMLElement と TextNode は内部のみ。 + * @return {Node} 自身。チェインメソッド + * @example node.swap( newNode ); + */ +function X_Node_swap( v ){ if( !this.parent ) return this; - return arguments.length === 1 ? this.before( v ).remove() : this.before.apply( this, arguments ).remove(); + return arguments.length === 1 ? this[ 'prev' ]( v )[ 'remove' ]() : X_Node_prev.apply( this, arguments )[ 'remove' ](); }; -/* -------------------------------------- - * Remove +/** + * 要素を抜く。 + * @alias Node.prototype.remove + * @return {Node} 自身。チェインメソッド + * @example node.remove(); */ function X_Node_remove(){ - var parent = this.parent; + var parent = this.parent, + elm; if( !parent ) return this; - parent._xnodes.splice( parent._xnodes.indexOf( this ), 1 ); - if( this._root ){ - X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; - X_Node_reserveUpdate(); - }; delete this.parent; - delete this._root; + parent[ '_xnodes' ].splice( parent[ '_xnodes' ].indexOf( this ), 1 ); + + if( this[ '_flags' ] & X_Node_State.IN_TREE ){ + this[ '_flags' ] &= ~X_Node_State.IN_TREE & ~X_Node_State.IE5_DISPLAY_NONE_FIX; + this[ '_xnodes' ] && X_Node_toggleInTreeFlag( this[ '_xnodes' ], false ); + + if( X_UA_DOM.IE4 ){ + elm = this[ '_rawObject' ] || X_Node__ie4getRawNode( this ); + if( elm ){ + X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; + X_Node_reserveUpdate(); + } else + if( !this[ '_tag' ] && ( parent[ '_flags' ] & X_Node_State.IE4_FIXED ) === 0 ){ + parent[ '_flags' ] |= X_Node_State.IE4_DIRTY_CHILDREN; + }; + } else { + elm = this[ '_rawObject' ]; + if( elm && elm.parentNode && elm.parentNode.tagName ){ + X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; + X_Node_reserveUpdate(); + }; + }; + }; return this; }; +/** + * 子要素を破棄する。子要素は kill() されます。 + * @alias Node.prototype.empty + * @return {Node} 自身。チェインメソッド + * @example node.empty(); + */ function X_Node_empty(){ - var xnodes = this._xnodes, i; + var xnodes = this[ '_xnodes' ], i; if( xnodes && ( i = xnodes.length ) ){ for( ; i; ){ - xnodes[ --i ].destroy(); + xnodes[ --i ][ 'kill' ](); }; xnodes.length = 0; }; return this; }; -/* -------------------------------------- - * destory - */ -// Node._destroyChildFlag = false; // TODO - -function X_Node_destroy( isChild ){ - var xnodes = this._xnodes, i, elm; - - if( !this._state ) return; +function X_Node_onBeforeKill( e ){ + var xnodes = this[ '_xnodes' ], i, elm; - elm = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); + if( ( this[ '_flags' ] & X_Node_State.EXIST ) === 0 ) return X_Callback_NONE; + elm = this[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); + elm && this[ '_listeners' ] && X_EventDispatcher_unlistenAll( this ); // イベントの退避 + if( xnodes && ( i = xnodes.length ) ){ - //for( ; i; ){ - // xnodes[ --i ].destroy( true ); - //}; + for( ; i; ){ + X_Node_onBeforeKill.call( xnodes[ --i ] ); + }; }; - elm && this._listeners && this.unlisten(); // イベントの退避 - delete X_Node_CHASHE[ this._uid ]; - delete this._state; - - if( this._root ){ - !isChild && this.remove(); - } else { - this.parent && this.parent._xnodes.splice( this.parent._xnodes.indexOf( this ), 1 ); - elm && !isChild && X_Node__actualRemove( this ); - this.kill(); + delete X_Node_CHASHE[ this[ '_uid' ] ]; + + if( e ){ + this[ 'remove' ](); + if( X_Node_reserveRemoval[ X_Node_reserveRemoval.length - 1 ] === this ){ + this[ '_flags' ] &= ~X_Node_State.EXIST; + return X_Callback_PREVENT_DEFAULT; + }; }; + return X_Callback_NONE; }; - -/* -------------------------------------- - * contains +/** + * 要素を子以下に持つか?調べる。 + * @alias Node.prototype.contains + * @param {Node|string|HTMLElement|TextNode} [v] HTMLElement と TextNode は内部のみ。 + * @return {boolean} + * @example node.contains( testNode ); */ function X_Node_contains( v ){ var elm, type, xnodes, i; - if( !v || this._xnodeType !== 1 ) return false; + 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_Node_TYPE.RAW_HTML || type === X_Node_TYPE.RAW_TEXT ) ){ - return elm.contains( v ); + if( ( elm = this[ '_rawObject' ] || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ) ) && document.contains && ( type = X_Node_getType( v ) ) && ( type === X_Node_TYPE.RAW_HTML || type === X_Node_TYPE.RAW_TEXT ) ){ + return elm.contains( v ); }; + //if( document.compareDocumentPosition ){ // //}; - xnodes = this._xnodes; + xnodes = this[ '_xnodes' ]; if( !xnodes || !xnodes.length ) return false; if( xnodes.indexOf( v ) !== -1 ) return true; // fast - if( elm === v.parentNode ) return false; for( i = xnodes.length; i; ){ - if( xnodes[ --i ].contains( v ) ) return true; + if( xnodes[ --i ][ 'contains' ]( v ) ) return true; }; return false; }; -/* -------------------------------------- - * getChild +/** + * index の子要素を取得する。 + * @alias Node.prototype.getChildAt + * @param {number} index 取得する子ノードの位置。0~ + * @return {Node} 子要素 + * @example child1 = parent.getChildAt(1); */ function X_Node_getChildAt( i ){ - var xnodes = this._xnodeType === 1 && this._xnodes; + var xnodes = this[ '_xnodes' ]; return xnodes && 0 <= i && i < xnodes.length && xnodes[ i ]; }; -function X_Node_length(){ - var xnodes = this._xnodes; +/** + * 子要素の数を取得する。 + * @alias Node.prototype.numChildren + * @return {number} 子要素の数。 + * @example n = parent.numChildren(); + */ +function X_Node_numChildren(){ + var xnodes = this[ '_xnodes' ]; return xnodes ? xnodes.length : 0; }; -/* -------------------------------------- - * firstChild, lastChild +/** + * 最初の子要素を取得する。 + * @alias Node.prototype.firstChild + * @return {Node} 最初の子要素 + * @example child0 = parent.firstChild(); */ function X_Node_firstChild(){ - return this.getChildAt( 0 ); + return this[ '_xnodes' ] && this[ '_xnodes' ][ 0 ]; }; + +/** + * 最後の子要素を取得する。 + * @alias Node.prototype.lastChild + * @return {Node} 最後の子要素 + * @example lastChild = parent.lastChild(); + */ function X_Node_lastChild(){ - return this.getChildAt( this._xnodes.length - 1 ); + var xnodes = this[ '_xnodes' ]; + return xnodes && xnodes[ xnodes.length - 1 ]; }; -/* -------------------------------------- - * getOrder +/** + * 要素の index 位置を取得する。 + * @alias Node.prototype.getOrder + * @return {number} index -1 の場合、親を持たない。 + * @example index = node.getOrder(); */ function X_Node_getOrder(){ var parent = this.parent; - if( !parent ) return -1; - return parent._xnodes.indexOf( this ); + return this === X_Node_html ? + 0 : + parent ? + parent[ '_xnodes' ].indexOf( this ) : + -1; }; -/* -------------------------------------- - * className, addClass, removeClass, hasClass +/** + * className の取得と設定。 + * @alias Node.prototype.className + * @return {string|Node} getter の場合 class 文字列、setter の場合自身。 + * @example // getter + * className = node.className(); + * // setter + * node.className( 'myClass myClass_new' ); */ function X_Node_className( v ){ var node, _, __; // getter - if( v === undefined ) return this._className; + if( v === undefined ) return this[ '_className' ]; // setter - if( this._className === v ) return this; - if( !v || typeof v !== 'string' ){ - delete this._className; + if( this[ '_className' ] === v ) return this; + if( !v || !X_Type_isString( v ) ){ + delete this[ '_className' ]; } else { // cleanup _ = ' '; @@ -724,35 +996,53 @@ function X_Node_className( v ){ v.charAt( 0 ) === _ && ( v = v.substr( 1 ) ); v.lastIndexOf( _ ) === 0 && ( v = v.substr( 0, v.length - 1 ) ); - if( this._className === v ) return this; - v ? ( this._className = v ) : delete this._className; + if( this[ '_className' ] === v ) return this; + v ? ( this[ '_className' ] = v ) : delete this[ '_className' ]; }; - this._dirty |= X_Node_Dirty.CLASSNAME; - this._root && X_Node_reserveUpdate(); + this[ '_flags' ] |= X_Node_State.DIRTY_CLASSNAME; + this[ '_flags' ] & X_Node_State.IN_TREE && X_Node_reserveUpdate(); return this; }; + +/** + * className の追加。 + * @alias Node.prototype.addClass + * @param {string} className スペース区切りで複数のクラスを追加できる。 + * @return {Node} 自身。 + * @example node.addClass( 'myClass myClass_new' ); + */ function X_Node_addClass( v ){ - var names = v.split( ' ' ), - i = names.length, + var names = v.split( ' ' ), + i = names.length, + _class = this[ '_className' ], name; v = ''; for( ; i; ){ name = names[ --i ]; if( !name ) continue; - !this.hasClass( name ) && ( v += ( v ? ' ' : '' ) + name ); + !this[ 'hasClass' ]( name ) && ( v += ( v ? ' ' : '' ) + name ); }; - return v ? this.className( this._className + ( this._className ? ' ' : '' ) + v ) : this; + return v ? this[ 'className' ]( ( _class ? _class + ' ' : '' ) + v ) : this; }; + +/** + * className の削除。 + * @alias Node.prototype.removeClass + * @param {string} className スペース区切りで複数のクラスを削除できる。 + * @return {Node} 自身。 + * @example node.removeClass( 'myClass myClass_new' ); + */ function X_Node_removeClass( v ){ - var _ = ' ', - className = this._className, - names = v.split( _ ), + var _ = ' ', + _class = this[ '_className' ], + names = v.split( _ ), classNames, i, f, j; - if( !className ) return this; - for( classNames = className.split( _ ), i = classNames.length; i; ){ - className = classNames[ --i ]; + + if( !_class ) return this; + for( classNames = _class.split( _ ), i = classNames.length; i; ){ + _class = classNames[ --i ]; for( j = names.length; j; ){ - if( className === names[ --j ] ){ + if( _class === names[ --j ] ){ classNames.splice( i, 1 ); names.splice( j, 1 ); f = true; @@ -760,110 +1050,143 @@ function X_Node_removeClass( v ){ }; }; }; - return f ? this.className( classNames.join( _ ) ) : this; + return f ? this[ 'className' ]( classNames.join( _ ) ) : this; }; + +/** + * className の更新。 + * @alias Node.prototype.toggleClass + * @param {string} className スペース区切りで複数のクラスを削除できる。 + * @param {boolean} [opt_toggle=] true はクラスの追加。false はクラスの削除。undefined はクラスのトグル。 + * @return {Node} 自身。 + * @example node.toggleClass( 'myClass myClass_new', !!n ); + */ function X_Node_toggleClass( v, opt_toggle ){ var names, i, name; if( opt_toggle !== undefined ){ - return !!opt_toggle ? this.addClass( v ) : this.removeClass( v ); + return !opt_toggle ? this[ 'removeClass' ]( v ) : this[ 'addClass' ]( v ); }; names = v.split( ' ' ); for( i = names.length; i; ){ name = names[ --i ]; - this.hasClass( name ) ? this.removeClass( name ) : this.addClass( name ); + this[ 'hasClass' ]( name ) ? this[ 'removeClass' ]( name ) : this[ 'addClass' ]( name ); }; return this; }; + +/** + * className を持つか。 + * @alias Node.prototype.hasClass + * @param {string} className スペース区切りで複数のクラスを削除できる。 + * @return {boolean} + * @example node.hasClass( 'myClass myClass_new' ); + */ function X_Node_hasClass( v ){ var _ = ' ', - className = this._className, + _class = this[ '_className' ], i, name; - if( className === v ) return true; - if( !className ) return false; + if( _class === v ) return true; + if( !_class ) return false; - className = _ + className + _; - if( className.indexOf( _ + v + _ ) !== -1 ) return true; // lucky hit + _class = _ + _class + _; + if( _class.indexOf( _ + v + _ ) !== -1 ) return true; // lucky hit for( v = v.split( _ ), i = v.length; i; ){ name = v[ --i ]; if( name === '' ) continue; - if( className.indexOf( _ + name + _ ) === -1 ) return false; + if( _class.indexOf( _ + name + _ ) === -1 ) return false; }; return true; }; -/* -------------------------------------- - * html, text +/** + * innerHTML 取得・設定。outerHTML が欲しい場合は、xnode.call('outerHTML') とできる。 + * @alias Node.prototype.html + * @param {string} [html=] html文字列 + * @return {string|Node} + * @example node.html( '' ); */ - function X_Node_html( html ){ var _ = '', q = '"', xnodes, n, i, l; // setter if( html !== undefined ){ // String 以外に Number や false null なども許可 - if( this._xnodeType === 3 ) return this.text( html ); - return html ? this.empty().append.apply( this, X_HtmlParser_parse( html, true ) ) : this.empty(); + if( !this[ '_tag' ] ) return this[ 'text' ]( html ); + return html ? this[ 'empty' ]()[ 'append' ].apply( this, X_HtmlParser_parse( html, true ) ) : this[ 'empty' ](); }; // getter - if( this._xnodeType === 3 ){ - return this._text; + if( !this[ '_tag' ] ){ + return this[ '_text' ]; }; - if( this._dirty & X_Node_Dirty.CSS && !( this._cssText = X_Node_CSS_objToCssText( this ) ) ){ - delete this._cssText; - }; + this[ '_flags' ] & X_Node_State.OLD_CSSTEXT && X_Node_CSS_objToCssText( this ); + html = !X_Node_outerXNode ? [] : [ - '<', this._tag, - this._id ? ' id="' + this._id + q : _, - this._className ? ' class="' + this._className + q : _, - this._attrText === false ? ( this._attrText = X_Node_Attr_objToAttrText( this._attrs ) ) : this._attrText, - this._cssText ? ' style="' + this._cssText + q : _, + '<', this[ '_tag' ], + this[ '_id' ] ? ' id="' + this[ '_id' ] + q : _, + this[ '_className' ] ? ' class="' + this[ '_className' ] + q : _, + this[ '_flags' ] & X_Node_State.OLD_ATTRTEXT ? X_Node_Attr_objToAttrText( this ) : this[ '_attrText' ], + this[ '_cssText' ] ? ' style="' + this[ '_cssText' ] + q : _, '>' ]; n = html.length; - if( ( xnodes = this._xnodes ) && ( l = xnodes.length ) ){ + if( ( xnodes = this[ '_xnodes' ] ) && ( l = xnodes.length ) ){ if( !X_Node_outerXNode ) X_Node_outerXNode = this; for( i = 0; i < l; ++i ){ - html[ n ] = xnodes[ i ].html(); + html[ n ] = xnodes[ i ][ 'html' ](); ++n; }; - if( X_Node_outerXNode === this ) X_Node_outerXNode = null; + if( X_Node_outerXNode === this ) X_Node_outerXNode = null; }; - !X_Node_outerXNode || X_Dom_DTD_EMPTY[ this._tag ] || ( html[ n ] = '<\/' + this._tag + '>' ); + !X_Node_outerXNode || X_Dom_DTD_EMPTY[ this[ '_tag' ] ] || ( html[ n ] = '<\/' + this[ '_tag' ] + '>' ); return html.join( _ ); }; +/* + * null が来たら '', 数値等が来たら文字列化 + */ +/** + * textContent 取得・設定。null が来たら '', 数値等が来たら文字列化 + * @alias Node.prototype.text + * @param {string} [text=] + * @return {string|Node} + * @example node.text( 'Hello, world!' ); + */ function X_Node_text( text ){ var xnodes, texts, i, l; // setter if( text !== undefined ){ - if( this._xnodeType === 3 ){ - if( this._text !== text ){ - text ? ( this._text = text ) : delete this.text; - this._root && X_Node_reserveUpdate(); - this._dirty |= X_Node_Dirty.CONTENT; + if( text === null ) text = ''; + text += ''; + + if( !this[ '_tag' ] ){ + if( this[ '_text' ] !== text ){ + text ? ( this[ '_text' ] = text ) : delete this[ '_text' ]; + this[ '_flags' ] |= X_Node_State.DIRTY_CONTENT; + this[ '_flags' ] & X_Node_State.IN_TREE && X_Node_reserveUpdate(); }; return this; }; - if( !text ) return this.empty(); - if( ( xnodes = this._xnodes ) && xnodes.length === 1 && xnodes[ 0 ]._xnodeType === 3 ){ - xnodes[ 0 ].text( text ); + if( ( xnodes = this[ '_xnodes' ] ) && xnodes.length === 1 && !xnodes[ 0 ][ '_tag' ] ){ + xnodes[ 0 ][ 'text' ]( text ); return this; }; - this.empty().createText( text ); + // TODO 一つのtextnode を残すケース 完全に削除したい場合は empty()を使う + if( !text ) return this[ 'empty' ](); + this[ 'empty' ]()[ 'createText' ]( text ); return this; }; // getter - if( this._xnodeType === 1 ){ - if( ( xnodes = this._xnodes ) && ( l = xnodes.length ) ){ + if( this[ '_tag' ] ){ + if( ( xnodes = this[ '_xnodes' ] ) && ( l = xnodes.length ) ){ for( texts = [], i = 0; i < l; ++i ){ - texts[ i ] = xnodes[ i ].text(); + texts[ i ] = xnodes[ i ][ 'text' ](); }; return texts.join( '' ); }; return ''; }; - return this._text; + return this[ '_text' ]; }; /* @@ -872,14 +1195,36 @@ function X_Node_text( text ){ function X_Node_call( name /*, opt_args... */ ){ var l = arguments.length - 1, raw, func, args, params, i; - + + switch( name ){ + case 'nodeType' : + return this[ '_tag' ] ? 1 : 3; + case 'outerHTML' : + X_Node_outerXNode = X_Node_body; // == true ならなんでもよい。型を合わすために xbody にしている + v = this[ 'html' ](); + X_Node_outerXNode = null; + return v; + case 'treeIsDirty' : + return !!X_Node_updateTimerID; + case 'fontSize' : + return X_Node_CSS_getCharSize( this ); + case 'inGPU' : + return !!( this[ '_flags' ] & ( X_Node_State.GPU_NOW | X_Node_State.GPU_RELEASE_RESERVED ) ); + }; + X_Node_updateTimerID && X_Node_startUpdate(); - raw = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); + 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; + return; + }; + func = raw[ name ]; - if( X.Type.isFunction( func ) ){ + if( X_Type_isFunction( func ) ){ if( l ){ args = X_Object_cloneArray( arguments ); args.shift(); @@ -887,7 +1232,7 @@ function X_Node_call( name /*, opt_args... */ ){ }; return raw[ name ](); } else - if( X.Type.isUnknown( func ) ){ + if( X_Type_isUnknown( func ) ){ // typeof func === unknown に対策 // http://la.ma.la/blog/diary_200509031529.htm if( l ){ @@ -928,236 +1273,436 @@ function X_Node_each( func /*, opt_args */ ){ /* -------------------------------------- * Async commit update * - * state: - * 0 : no_rawObject - * 1 : no_parent - * 2 : no_root - * 3 : dirty - * 4 : clean - * - * remove : - * root._reserveRemoval = [] に追加。commitUpdate で remove して state は not_added - * add : - * root._reserveRemoval にいたら消す, new_parent._xnodes に挿入 steta は not_added にして commitUpdate を待つ + * TODO Timer や DOM イベントの呼び出しの最後に、まだ一度も commitUpdate していないなら commitUpdate してしまう。 */ function X_Node_reserveUpdate(){ - if( !X_Node_updateTimerID ) X_Node_updateTimerID = X.Timer.requestFrame( X_Node_startUpdate ); + if( !X_Node_updateTimerID ) X_Node_updateTimerID = X_Timer_requestFrame( X_Node_startUpdate ); }; +var X_Node_updateReservedByReleaseGPU = false; + function X_Node_startUpdate( time ){ - var removal, i, xnode, tmp; + var removal, i, xnode; - if( !X_Node_updateTimerID || X_ViewPort_readyState < X_TEMP.SYSTEM_EVENT_INIT ){ + if( !X_Node_updateTimerID || X_ViewPort_readyState < X_EVENT_INIT ){ return; }; - X.Timer.cancelFrame( X_Node_updateTimerID ); + X_Timer_cancelFrame( X_Node_updateTimerID ); X_Node_updateTimerID = 0; if( time ){ // X.Timer 経由でないと発火しない このイベントでサイズを取ると無限ループに - X_System._listeners && X_System._listeners[ X.Event.BEFORE_UPDATE ] && X_System.dispatch( X.Event.BEFORE_UPDATE ); + X_System[ '_listeners' ] && X_System[ '_listeners' ][ X_EVENT_BEFORE_UPDATE ] && X_System[ 'dispatch' ]( X_EVENT_BEFORE_UPDATE ); }; removal = X_Node_reserveRemoval; - - //tmp = X_Node_body._rawObject.style.visibility; - //this._rawObject.style.visibility = 'hidden'; - - //console.log( '_actualRemove().' ); if( i = removal.length ){ for( ; i; ){ xnode = removal[ --i ]; + // TODO GPU レイヤーの子の場合、remove をスキップする。 非GPU レイヤーへ apppend される場合、clone する? X_Node__actualRemove( xnode ); - !X_Node_body._state && xnode.kill(); + ( xnode[ '_flags' ] & X_Node_State.EXIST ) === 0 && xnode[ 'kill' ](); }; removal.length = 0; }; - - //console.log( 'start _startUpdate().' ); - - /* X_Node_html._dirty ? */ X_Node__commitUpdate( X_Node_html ); /* : X_Node__commitUpdate( X_Node_body ); */; - //console.log( 'end of _startUpdate().' ); + 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' ] ); + } 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' ] ); + }; + + if( X_Node_updateReservedByReleaseGPU ){ + X_Node_reserveUpdate(); + X_Node_updateReservedByReleaseGPU = false; + }; if( time ){ // X.Timer 経由でないと発火しない このイベントでサイズを取ると無限ループに - X_System._listeners && X_System._listeners[ X.Event.UPDATED ] && X_System.dispatch( X.Event.UPDATED ); + 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 ); }; - X_ViewPort._listeners && X_ViewPort._listeners[ X.Event.AFTER_UPDATE ] && X_ViewPort.asyncDispatch( X.Event.AFTER_UPDATE ); - //this._rawObject.style.visibility = tmp; + X_ViewPort[ '_listeners' ] && X_ViewPort[ '_listeners' ][ X_EVENT_AFTER_UPDATE ] && X_ViewPort[ 'asyncDispatch' ]( X_EVENT_AFTER_UPDATE ); }; +/* + * 1. GPU_NOW の場合、子の変更は行わない + * 2. GPU解放予約 の場合//、この要素のみ変更を行う。rAF 後にさらに更新するためフラグを立てる。 + * 3. GPU予約 -> GPU + * 4. style="display:none" の場合、これ以下の変更を行わない。 + * 5. ie5 非表示フラグが立っていて、親と自身の class・id によって非表示になっていて、親と自身に変更がない。accumulatedFlags を使用。 + * -> TODO これ TREE の変更を検出できない。 remove したときに 子まで X_Node_State.IE5_DISPLAY_NONE_FIXを落とす。 + * 6. 要素の生成 + * 7. 要素の位置のズレを補正 + * 8. 更新の適用 + * 9. ie5 親及び自身へのクラス・id指定で display:none になるケースがありそれを検出。 + * このままでは、生成と破棄が繰り返されてしまうので親と自身のクラス・idが変わった場合、ツリー位置の変化があった場合に再生する。 + */ var X_Node__commitUpdate = X_UA_DOM.W3C ? - ( function( that, parentElement, nextElement ){ - var elm = that._rawObject, - xnodes, l, i, frg, next, k, v; - - if( that._state & X_Node_State.IE5_DISPLAY_NONE_FIX ){ - //alert( that._tag + ' ' + !!elm ); - // filter の効いている要素だけdisplay:none が無視される模様。filter を切ればよい? - // 親が、display:none の場合は? - elm && elm.parentNode && X_Node__actualRemove( that ); - return nextElement; + ( function( that, parentElement, nextElement, accumulatedFlags ){ + var elm = that[ '_rawObject' ], + created, xnodes, l, next; + + // 1. GPU 一切の更新をスキップ + if( that[ '_flags' ] & X_Node_State.GPU_NOW ){ + console.log( '更新のskip ' + !!( that[ '_flags' ] & X_Node_BitMask_IS_DIRTY ) ); + that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); + return elm; }; - if( !elm || ( parentElement && elm.parentNode !== parentElement ) || ( nextElement && elm.nextSibling !== nextElement ) ){ - nextElement ? - parentElement.insertBefore( X_Node__actualCreate( that ), nextElement ) : - parentElement.appendChild( X_Node__actualCreate( that ) ); - X_Node__afterActualCreate( that ); + // 2. GPU解放予約 + if( that[ '_flags' ] & X_Node_State.GPU_RELEASE_RESERVED ){ + // console.log( 'GPU 解放 ' ); + //X_Node_updateReservedByReleaseGPU = true; + //X_Node__updateRawNode( that, elm ); + that[ '_flags' ] &= X_Node_BitMask_RESET_GPU; + //return elm;// TODO もしかしたらこのタイミングで更新できるかも。 + }; - return elm || that._rawObject; - } else - if( ( xnodes = that._xnodes ) && ( l = xnodes.length ) ) { + // 3. GPU予約 -> GPU + if( that[ '_flags' ] & X_Node_State.GPU_RESERVED ){ + that[ '_flags' ] &= X_Node_BitMask_RESET_GPU; + that[ '_flags' ] |= X_Node_State.GPU_NOW; + }; + + // 4. style="display:none" の場合 + if( that[ '_flags' ] & X_Node_State.STYLE_IS_DISPLAY_NONE ){ + if( X_Node_displayNoneFixForIE5 ){ + // filter の効いている要素を含む要素は display:none が無視される。 + // filter = '' で削除はできるが、再表示時に filter が消える。 -> filter な要素を削除してしまう。 + if( elm && elm.parentNode ){ + X_Node__actualRemove( that ); + }; + return nextElement; + }; + elm && ( elm.style.display = 'none' ); + return ( elm && elm.nextSibling === nextElement ) ? elm : nextElement; + }; + + // 5. ie5 非表示fixフラグ + accumulatedFlags |= that[ '_flags' ]; + + if( that[ '_flags' ] & X_Node_State.IE5_DISPLAY_NONE_FIX ){ + if( accumulatedFlags & ( X_Node_State.DIRTY_POSITION | X_Node_State.DIRTY_ID | X_Node_State.DIRTY_CLASSNAME ) === 0 ){ + return nextElement; + }; + }; + + // 6. 要素の生成 + 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' ] ){ + elm[ 'UID' ] = that[ '_uid' ]; + }; + } else + if( X_Node_strictElmCreation ){ + that[ '_flags' ] & X_Node_State.OLD_CSSTEXT && X_Node_CSS_objToCssText( that, true ); // OLD_CSSTEXT ?? + + that[ '_rawObject' ] = elm = + document.createElement( [ + '<', that[ '_tag' ], + ' UID="', that[ '_uid' ], '"', + that[ '_id' ] ? ' id="' + that[ '_id' ] + '"' : '', + that[ '_className' ] ? ' class="' + that[ '_className' ] + '"' : '', + X_Node_Attr_objToAttrText( that, true ), + that[ '_cssText' ] ? ' style="' + that[ '_cssText' ] + '"' : '', + '>' ].join( '' ) ); + } else { + that[ '_rawObject' ] = elm = document.createElement( that[ '_tag' ] ); + }; - /*if( elm.childNodes.length !== l && ( frg = X_Node_useDocumentFragment ) ){ - for( i = 0; i < l; ++i ){ - frg.appendChild( X_Node__actualCreate( xnodes[ i ], true ) ); + // IE には要素追加のタイミングで起こるメモリリークがありここで追加 + if( !X_Node_addTreeAfterChildren ){ + nextElement ? + parentElement.insertBefore( elm, nextElement ) : + parentElement.appendChild( elm ); + }; + + if( that[ '_tag' ] ){ + X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 + that[ '_flags' ] |= X_Node_State.ACTUAL_LISTENING; + + if( X_Node_documentFragment ){ + //( frg = X_Node_documentFragment ).appendChild( elm ); + // 連続する要素の差し替えの場合に有効 }; - elm.appendChild( frg ); - } else {*/ - for( ; l; ){ - next = X_Node__commitUpdate( xnodes[ --l ], elm, next ); + + if( X_Node_strictElmCreation ){ + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; + // ie の string から要素を作る場合、ネットワーク系属性は onload イベントなどを拾うために、要素生成->イベント復帰後に適用する + that[ '_newAttrs' ] && ( that[ '_flags' ] |= X_Node_State.DIRTY_ATTR ); // _newAttrs には ネットワーク系属性が入っている。Network 系の属性は遅らせて設定 + that[ '_flags' ] |= X_Node_State.DIRTY_IE_FILTER;// doc 追加後に filter を指定しないと有効にならない。 + } else { + elm[ 'UID' ] = that[ '_uid' ]; + that[ '_newAttrs' ] = that[ '_attrs' ]; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; + that[ '_flags' ] |= X_Node_State.DIRTY_ID | X_Node_State.DIRTY_CLASSNAME | X_Node_State.DIRTY_ATTR | X_Node_State.DIRTY_CSS | X_Node_State.DIRTY_IE_FILTER; + + // http://outcloud.blogspot.jp/2010/09/iframe.html + // この問題は firefox3.6 で確認 + if( X_UA[ 'Gecko' ] ){ + if( that[ '_tag' ] === 'IFRAME' && ( !that[ '_attrs' ] || !that[ '_attrs' ][ 'src' ] ) ){ + //elm.contentWindow.location.replace = elm.src = 'about:blank'; + that[ 'attr' ]( 'src', 'about:blank' ); + }; + }; }; - //}; + }; + + created = true; + } else + // 7. 要素の位置のズレを補正 + if( elm.parentNode !== parentElement || ( nextElement && elm.nextSibling !== nextElement ) ){ + nextElement ? + parentElement.insertBefore( elm, nextElement ) : + parentElement.appendChild( elm ); + }; + + if( that[ '_listeners' ] && ( that[ '_flags' ] & X_Node_State.ACTUAL_LISTENING ) === 0 ){ + X_EventDispatcher_toggleAllEvents( that, true );// イベントの退避 + that[ '_flags' ] |= X_Node_State.ACTUAL_LISTENING; + }; + + // 8. 更新の適用 + if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ){ + delete that[ '_fontSize' ]; + X_Node__updateRawNode( that, elm ); }; - delete that._fontSize; - that._dirty && X_Node__updateRawNode( that, elm ); - if( that._state & X_Node_State.IE5_DISPLAY_NONE_FIX ){ - return nextElement; + // 9. ie5 only + // 親及び自身へのクラス・id指定で display : none になるケースがありそれを検出 + // 生成と破棄が繰り返されてしまう、親と自身の id, class が変わった場合だけ再生成。 accumulatedFlags & ( ID | CLASSNAME ) + // currentStyle を観ていたときはエラーで停止する、alert と挟むと正常に動いて支離滅裂 + if( X_Node_displayNoneFixForIE5 && that[ '_tag' ] ){ + if( elm.runtimeStyle.display === 'none' ){ + X_Node__actualRemove( that ); + that[ '_flags' ] |= X_Node_State.IE5_DISPLAY_NONE_FIX; + return nextElement; + } else { + that[ '_flags' ] &= ~X_Node_State.IE5_DISPLAY_NONE_FIX; + }; }; + + // 10. 子要素の更新。 + if( ( xnodes = that[ '_xnodes' ] ) && ( l = xnodes.length ) ) { + for( ; l; ){ + next = X_Node__commitUpdate( xnodes[ --l ], elm, next, accumulatedFlags ); + }; + }; + + if( created && X_Node_addTreeAfterChildren ){ + nextElement ? + parentElement.insertBefore( elm, nextElement ) : + parentElement.appendChild( elm ); + + if( X_UA[ 'Gecko' ] && that[ '_tag' ] === 'IFRAME' && elm.contentWindow ){ + // tree に追加されるまで contentWindow は存在しない。 + elm.contentWindow.location.replace = elm.src; + }; + }; + return elm; }) : X_UA_DOM.IE4 ? - ( function( that, parentElement, prevElement ){ - var elm = that._rawObject || X_Node__ie4getRawNode( that ), - xnodes, l, i, html, text, prev; + ( function( that, parentElement, prevElement, accumulatedFlags ){ + var elm = that[ '_rawObject' ] || X_Node__ie4getRawNode( that ), + xnodes, l, i, dirty, mix, html, text, prev; + if( !that[ '_tag' ] ){ + that[ '_flags' ] & X_Node_State.DIRTY_CONTENT && X_Node__updateRawNode( that, elm ); + return elm; + }; + + // 4. style="display:none" の場合 + if( that[ '_flags' ] & X_Node_State.STYLE_IS_DISPLAY_NONE ){ + if( elm ){ + elm.style.display = 'none'; + if( elm.style.display !== 'none' ){ // ie4 の style は currentStyle 相当らしい、、、? div 以外への display:none が効かないので remove する。 + X_Node__actualRemove( that ); + return prevElement; + }; + }; + return elm || prevElement; + }; + if( !elm ){ prevElement ? - prevElement.insertAdjacentHTML( 'AfterEnd', X_Node__actualCreate( that ) ) : - parentElement.insertAdjacentHTML( 'AfterBegin', X_Node__actualCreate( that ) ); + 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 ); + return that[ '_rawObject' ] || X_Node__ie4getRawNode( that ); }; - xnodes = that._xnodes; + accumulatedFlags |= that[ '_flags' ]; + + xnodes = that[ '_xnodes' ]; l = xnodes ? xnodes.length : 0; + dirty = !!( that[ '_flags' ] & X_Node_State.IE4_DIRTY_CHILDREN ); - if( that._dirty & X_Node_Dirty.IE4_TEXTNODE_FIX || ( that._state & X_Node_State.IE4_ONLY_TEXT && ( l !== 1 || xnodes[ 0 ]._xnodeType !== 3 ) ) ){ // 1 < l && elm.children.length === 0 + /* + * HTML の下に TextNode だけ 。MIX_FIXED でない場合、削除、追加 を親に通知 + * HTML の下に HTML だけ + * HTML の下は MIX -> TextNode, html の削除、変更、追加 + * HTML の下は MIX_FIXED -> TextNode を に置き換えてあるのでW3C DON 的に触ることができる + */ + if( dirty ){ + that[ '_flags' ] &= ~X_Node_State.IE4_DIRTY_CHILDREN; + for( i = 0; i < l; ++i ){ + if( xnodes[ i ][ '_tag' ] ){ + that[ '_flags' ] |= X_Node_State.IE4_HAS_ELEMENT; + } else { + that[ '_flags' ] |= X_Node_State.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_State.IE4_FIXED || that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_State.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 ] ); + html[ i ] = X_Node__actualCreate( xnodes[ i ], false ); }; elm.innerHTML = html.join( '' ); for( i = 0; i < l; ++i ){ X_Node__afterActualCreate( xnodes[ i ] ); }; - that._state &= ~X_Node_State.IE4_ONLY_TEXT; - } else - if( that._state & X_Node_State.IE4_ONLY_TEXT ){ // textNode が swap した場合の検出は、_root で行う - text = xnodes[ 0 ]; - if( text._dirty || !text._root ){ - elm.innerHTML = text._text; - delete text._dirty; - text._root = that._root; - }; + that[ '_flags' ] |= X_Node_State.IE4_FIXED; } else - if( l ){ + if( that[ '_flags' ] & X_Node_State.IE4_HAS_TEXTNODE ){ + dirty = dirty || false; for( i = 0; i < l; ++i ){ - prev = X_Node__commitUpdate( xnodes[ i ], elm, prev ); + 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' ](); }; - delete that._fontSize; - that._dirty && X_Node__updateRawNode( that, elm ); + if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ) delete that[ '_fontSize' ]; + + that[ '_flags' ] &= ~X_Node_State.DIRTY_POSITION; + that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); return elm; }) : (function(){}); +/* + * GPU レイヤーするブラウザは、子要素から変更を当てていく? <- とりあえず、親要素から。 + */ var X_Node__updateRawNode = X_UA_DOM.W3C ? ( function( that, elm ){ var attrs, rename, k, v; // textNode - if( that._dirty & X_Node_Dirty.CONTENT ){ - elm.data = X_String_chrReferanceTo( that._text ); - delete that._dirty; + if( !that[ '_tag' ] ){ + elm.data = X_String_chrReferanceTo( that[ '_text' ] ); + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; return; }; // id - if( that._dirty & X_Node_Dirty.ID ){ - that._id ? ( elm.id = that._id ) : ( elm.id && elm.removeAttribute( 'id' ) ); + if( that[ '_flags' ] & X_Node_State.DIRTY_ID ){ + that[ '_id' ] ? ( elm.id = that[ '_id' ] ) : ( elm.id && elm.removeAttribute( 'id' ) ); }; // className - if( that._dirty & X_Node_Dirty.CLASSNAME ){ - that._className ? ( elm.className = that._className ) : ( elm.className && elm.removeAttribute( X_UA.IE5678 ? 'className' : 'class' ) ); // className は ie7-? - - // ie5 only - if( X_Node_State.IE5_DISPLAY_NONE_FIX && elm.currentStyle.display === 'none' ){ - X_Node__actualRemove( that ); - that._state |= X_Node_State.IE5_DISPLAY_NONE_FIX; - return; + if( that[ '_flags' ] & X_Node_State.DIRTY_CLASSNAME ){ + that[ '_className' ] ? ( elm.className = that[ '_className' ] ) : ( elm.className && elm.removeAttribute( X_UA[ 'IE' ] < 8 ? 'className' : 'class' ) ); // className は ie7- + }; + + // attr + if( that[ '_flags' ] & X_Node_State.DIRTY_ATTR && ( attrs = that[ '_newAttrs' ] || that[ '_attrs' ] ) ){ + rename = X_Node_Attr_renameForDOM; + + for( k in attrs ){ + v = attrs[ k ]; + + switch( that[ '_tag' ] + k ){ + case 'TEXTAREAvalue' : + // IETester 5.5 ではエラーが出なかった.MultipulIE5.5 ではエラーが出たので + if( !X_UA[ 'MacIE' ] && X_UA[ 'IE5x' ] ){ + elm.firstChild ? + ( elm.firstChild.data = v || '' ) : + elm.appendChild( document.createTextNode( v || '' ) ); + continue; + }; + break; + + case 'IFRAMEsrc' : + // http://outcloud.blogspot.jp/2010/09/iframe.html + // この問題は firefox3.6 で確認 + if( X_UA[ 'Gecko' ] && elm.contentWindow ){ + elm.contentWindow.location.replace = elm.src = v || ''; + continue; + }; + break; + + case 'IFRAMEname' : + // http://d.hatena.ne.jp/NeoCat/20080921/1221940658 + // こちらに名前をsetしないとtargetが動作しない + // これってあとから name を変更できないバグでは? itozyun + // if( X_UA[ 'IE' ] ) elm.name = elm.contentWindow.name = v || ''; + }; + + //if( X_EMPTY_OBJECT[ k ] ) continue; + // 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 ); }; + delete that[ '_newAttrs' ]; }; // style - // TODO display:none の場合、更新をスキップ - if( that._dirty & X_Node_Dirty.CSS ){ - if( that._cssText !== null || ( that._cssText = X_Node_CSS_objToCssText( that ) ) ){ - X_UA.Opera78 || X_UA.NN6 ? - elm.setAttribute( 'style', that._cssText ) : // opera8用 - ( elm.style.cssText = that._cssText ); + if( that[ '_flags' ] & X_Node_State.DIRTY_CSS ){ + if( that[ '_flags' ] & X_Node_State.OLD_CSSTEXT ? X_Node_CSS_objToCssText( that ) : that[ '_cssText' ] ){ + X_UA[ 'Opera78' ] || X_UA[ 'NN6' ] ? + elm.setAttribute( 'style', that[ '_cssText' ] ) : // opera8用 + ( elm.style.cssText = that[ '_cssText' ] ); } else { elm.style.cssText = ''; // IE5.5以下 Safari3.2 で必要 elm.removeAttribute( 'style' ); - delete that._cssText; }; - }; - - if( that._dirty & X_Node_Dirty.IE_FILTER ){ - elm.style.filter = X_Node_CSS_objToIEFilterText( that );; - }; - - // attr - // TODO display:none の場合、更新をスキップ - if( that._dirty & X_Node_Dirty.ATTR && ( attrs = that._newAttrs || that._attrs ) ){ - rename = X_Node_Attr_renameForDOM; - for( k in attrs ){ - if( !X_UA.MacIE && ( X_UA.IE5 || X_UA.IE55 ) ){ // IETester 5.5 ではエラーが出なかった.MultipulIE5.5 ではエラーが出たので - if( that._tag === 'TEXTAREA' && k === 'value' ){ - elm.firstChild ? - ( elm.firstChild.data = attrs[ k ] ) : - elm.appendChild( document.createTextNode( attrs[ k ] ) ); - continue; - }; - }; - // TODO IE では input, なぜか buttonも、type の変更が出来ない、object も 同値で置き換えようとしても不可 - ( v = attrs[ k ] ) === undefined ? - elm.removeAttribute( rename[ k ] || k ) : - ( elm[ rename[ k ] || k ] = X_Node_Attr_noValue[ k ] ? k : v ); - + } else + if( that[ '_flags' ] & X_Node_State.DIRTY_IE_FILTER ){ + v = X_Node_CSS_objToIEFilterText( that ); + if( v ){ + elm.style.filter = v; + that[ '_flags' ] |= X_Node_State.IE_FILTER_NOW; + } else { + elm.style.removeAttribute( 'filter' ); + that[ '_flags' ] &= ~X_Node_State.IE_FILTER_NOW; }; - delete that._newAttrs; }; - delete that._dirty; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }) : X_UA_DOM.IE4 ? ( function( that, elm ){ var attrs, rename, k, v; // fake textNode - if( that._dirty & X_Node_Dirty.CONTENT ){ - elm.innerText = that._text; - delete that._dirty; + if( !that[ '_tag' ] ){ + elm.innerText = that[ '_text' ]; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; return; }; @@ -1166,41 +1711,45 @@ var X_Node__updateRawNode = * title、className、id、lang、language には setAttribute でなく、element.id で直接読み書きできる */ // id - if( that._dirty & X_Node_Dirty.CONTENT ) elm.setAttribute( 'id', that._id || ( 'ie4uid' + xnode._uid ) ); + if( that[ '_flags' ] & X_Node_State.DIRTY_ID ) elm.setAttribute( 'id', that[ '_id' ] || ( 'ie4uid' + that[ '_uid' ] ) ); // className - if( that._dirty & X_Node_Dirty.CLASSNAME ){ - that._className ? ( elm.className = that._className ) : elm.removeAttribute( 'class' ); + if( that[ '_flags' ] & X_Node_State.DIRTY_CLASSNAME ){ + that[ '_className' ] ? ( elm.className = that[ '_className' ] ) : elm.removeAttribute( 'class' ); }; // style - if( that._dirty & X_Node_Dirty.CSS ){ - if( that._cssText !== null || ( that._cssText = X_Node_CSS_objToCssText( that ) ) ){ - elm.style.cssText = that._cssText; + if( that[ '_flags' ] & X_Node_State.DIRTY_CSS ){ + if( that[ '_flags' ] & X_Node_State.OLD_CSSTEXT ? X_Node_CSS_objToCssText( that ) : that[ '_cssText' ] ){ + elm.style.cssText = that[ '_cssText' ]; } else { elm.style.cssText = ''; elm.removeAttribute( 'style' ); - delete that._cssText; }; - }; - - if( that._dirty & X_Node_Dirty.IE_FILTER ){ - that._rawObject.style.filter = X_Node_CSS_objToIEFilterText( that );; + } else + if( that[ '_flags' ] & X_Node_State.DIRTY_IE_FILTER ){ + v = X_Node_CSS_objToIEFilterText( that ); + if( v ){ + elm.style.filter = v; + that[ '_flags' ] |= X_Node_State.IE_FILTER_NOW; + } else { + elm.style.removeAttribute( 'filter' ); + that[ '_flags' ] &= ~X_Node_State.IE_FILTER_NOW; + }; }; // attr - if( that._dirty & X_Node_Dirty.ATTR && ( attrs = that._newAttrs || that._attrs ) ){ + if( that[ '_flags' ] & X_Node_State.DIRTY_ATTR && ( attrs = that[ '_newAttrs' ] || that[ '_attrs' ] ) ){ rename = X_Node_Attr_renameForDOM; for( k in attrs ){ + //if( X_EMPTY_OBJECT[ k ] ) continue; ( v = attrs[ k ] ) === undefined ? elm.removeAttribute( rename[ k ] || k ) : - //that._tag === 'TEXTAREA' && k === 'value' ? - // ( elm.innerText = v ) : elm.setAttribute( rename[ k ] || k, X_Node_Attr_noValue[ k ] ? k : v ); }; - delete that._newAttrs; + delete that[ '_newAttrs' ]; }; - - delete that._dirty; + + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }) : (function(){}); @@ -1222,170 +1771,133 @@ var X_Node__updateRawNode = * document.createElement of ie4 is only for OPTION & IMAGE. */ var X_Node__actualCreate = - X_UA_DOM.W3C ? (function( that, isChild ){ - var elm = that._rawObject, - xnodes, frg, i, l; - - if( that._xnodeType === 3 ){ - if( elm ) return elm; - delete that._dirty; - return that._rawObject = document.createTextNode( X_String_chrReferanceTo( that._text ) ); - }; - - if( !elm ){ - if( that._dirty & X_Node_Dirty.CSS && !( that._cssText = X_Node_CSS_objToCssText( that ) ) ){ - delete that._cssText; - }; - that._isNew = true; - that._rawObject = elm = - X_Node_strictElmCreation ? - document.createElement( [ - '<', that._tag, - ' UID="', that._uid, '"', - that._id ? ' id="' + that._id + '"' : '', - that._className ? ' class="' + that._className + '"' : '', - that._attrText === false ? ( that._attrText = X_Node_Attr_objToAttrText( that._attrs ) ) : that._attrText, - that._cssText ? ' style="' + that._cssText + '"' : '', - '>' ].join( '' ) ) : - document.createElement( that._tag ); - }; - if( X_Node_useDocumentFragment ){ - if( ( xnodes = that._xnodes ) && ( l = xnodes.length ) ){ - !isChild && ( frg = X_Node_useDocumentFragment ).appendChild( elm ); - for( i = 0; i < l; ++i ){ - elm.appendChild( X_Node__actualCreate( xnodes[ i ], true ) ); - }; - return frg || elm; - }; - }; - - return elm; - }) : - X_UA_DOM.IE4 ? (function( that, isChild ){ - var uid = that._uid, + X_UA_DOM.IE4 && (function( that, isChild ){ + var uid = that[ '_uid' ], html, xnodes, n, i, l; - if( that._xnodeType === 3 ){ - html = [ '', that._text, '' ];// fake textNode - delete that._rawObject; + if( !that[ '_tag' ] ){ + html = [ '', that[ '_text' ], '' ];// fake textNode + delete that[ '_rawObject' ]; } else { - if( that._rawObject && !isChild ) X_Node__actualRemove( that, true ); + if( !isChild ) X_Node__actualRemove( that, /* true */ false ); - if( that._dirty & X_Node_Dirty.CSS && !( that._cssText = X_Node_CSS_objToCssText( that ) ) ){ - delete that._cssText; - }; + that[ '_flags' ] & X_Node_State.OLD_CSSTEXT && X_Node_CSS_objToCssText( that, true ); html = [ - '<', that._tag, ' id=', ( that._id || ( 'ie4uid' + uid ) ), ' UID="', uid, '"', - that._className ? ' class="' + that._className + '"' : '', - that._attrText === false ? ( that._attrText = X_Node_Attr_objToAttrText( that._attrs ) ) : that._attrText, - that._cssText ? ' style="' + that._cssText + '"' : '', + '<', that[ '_tag' ], ' id=', ( that[ '_id' ] || ( 'ie4uid' + uid ) ), ' UID="', uid, '"', + that[ '_className' ] ? ' class="' + that[ '_className' ] + '"' : '', + X_Node_Attr_objToAttrText( that, true ), + that[ '_cssText' ] ? ' style="' + that[ '_cssText' ] + '"' : '', '>' ]; n = html.length; - if( ( xnodes = that._xnodes ) && ( l = xnodes.length ) ){ - if( l === 1 && xnodes[ 0 ]._xnodeType === 3 ){ + if( ( xnodes = that[ '_xnodes' ] ) && ( l = xnodes.length ) ){ + + that[ '_flags' ] &= ~X_Node_State.IE4_DIRTY_CHILDREN; + for( i = 0; i < l; ++i ){ + if( xnodes[ i ][ '_tag' ] ){ + that[ '_flags' ] |= X_Node_State.IE4_HAS_ELEMENT; + } else { + that[ '_flags' ] |= X_Node_State.IE4_HAS_TEXTNODE; + }; + if( that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_BitMask_IE4_IS_MIX ){ + break; + }; + }; + + if( that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_State.IE4_HAS_TEXTNODE ){ // only textnode - html[ n ] = xnodes[ 0 ]._text; + html[ n ] = that[ 'text' ](); ++n; - that._state |= X_Node_State.IE4_ONLY_TEXT; } else { for( i = 0; i < l; ++i ){ html[ n ] = X_Node__actualCreate( xnodes[ i ], true ); ++n; - }; + }; + that[ '_flags' ] |= X_Node_State.IE4_FIXED; }; }; - X_Dom_DTD_EMPTY[ that._tag ] || ( html[ n ] = '<\/' + that._tag + '>' ); + X_Dom_DTD_EMPTY[ that[ '_tag' ] ] || ( html[ n ] = '<\/' + that[ '_tag' ] + '>' ); - delete that._newAttrs; + that[ '_newAttrs' ] && ( that[ '_flags' ] |= X_Node_State.DIRTY_ATTR ); }; return html.join( '' ); - }) : - (function(){}); + }); var X_Node__afterActualCreate = - X_UA_DOM.W3C ? (function( that ){ - var elm = that._rawObject, xnodes, l, attrs, k, i; - - that._root = that.parent._root; + X_UA_DOM.IE4 && (function( that ){ + var xnodes, i, v; - if( that._xnodeType === 3 ){ - that._dirty && X_Node__updateRawNode( that, elm ); - return that; - }; - - xnodes = that._xnodes; - l = xnodes && xnodes.length; + if( !that[ '_tag' ] ) return that; - if( that._isNew ){ - if( !X_Node_useDocumentFragment && l ){// docFrg が使えない場合、doc 追加後に子を追加 - for( i = 0; i < l; ++i ){ - elm.appendChild( X_Node__actualCreate( xnodes[ i ], true ) ); - }; - }; - if( X_Node_strictElmCreation ){ - if( that._dirty & X_Node_Dirty.IE_FILTER ){ - elm.style.filter = X_Node_CSS_objToIEFilterText( that );; - }; - delete that._dirty; - } else { - elm.UID = that._uid; - that._newAttrs = that._attrs; - that._dirty = X_Node_Dirty.ID | X_Node_Dirty.CLASSNAME | X_Node_Dirty.CSS | X_Node_Dirty.ATTR | X_Node_Dirty.IE_FILTER; - X_Node__updateRawNode( that, elm ); - }; - - delete that._isNew; - } else { - that._dirty && X_Node__updateRawNode( that, elm ); - }; - - for( i = 0; i < l; ++i ){ - X_Node__afterActualCreate( xnodes[ i ] ); - }; - // src の onload があるので先ではないか? - // TODO ie の str から要素を作る場合、srcだけ イベント設定後ではないか? - X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 - }) : - X_UA_DOM.IE4 ? (function( that ){ - var xnodes, i; - that._root = that.parent._root; - - if( that._xnodeType !== 1 ) return that; - - if( ( xnodes = that._xnodes ) && ( i = xnodes.length ) ){ + if( ( xnodes = that[ '_xnodes' ] ) && ( i = xnodes.length ) ){ for( ; i; ){ X_Node__afterActualCreate( xnodes[ --i ] ); }; }; - // textarea への value の適用はここで - if( that._dirty & X_Node_Dirty.IE_FILTER ){ - X_Node__ie4getRawNode( that ).style.filter = X_Node_CSS_objToIEFilterText( that );; + // ネットワーク系属性と filter は要素生成後に適用 + if( that[ '_flags' ] & ( X_Node_State.DIRTY_ATTR | X_Node_State.DIRTY_IE_FILTER ) ){ + X_Node__updateRawNode( that, that[ '_rawObject' ] || X_Node__ie4getRawNode( that ) ); + } else { + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }; - delete that._dirty; X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 - }) : - (function(){}); + }); var X_Node__actualRemove = X_UA_DOM.W3C ? + // GPUレイヤーにいるうちは remove しない。-> GPU解除してから remove する + // Firefox34 では遭遇せず、Safari で何度かアニメーションしているうちに発生 ( function( that, isChild ){ - var xnodes = that._xnodes, - elm = that._rawObject, + var xnodes = that[ '_xnodes' ], + elm = that[ '_rawObject' ], child, i, l; + if( xnodes && ( l = xnodes.length ) ){ for( i = 0; i < l; ++i ){ child = xnodes[ i ]; - child._xnodeType === 1 && X_Node__actualRemove( child, true ); + child[ '_tag' ] && X_Node__actualRemove( child, true ); }; }; if( !elm ) return; - that._listeners && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 - if( !X_UA.MacIE ){ - // elm.parentNode.tagName for ie7 -> that.state & BELONG_TREE_ACTUAL + + if( that[ '_flags' ] & X_Node_State.ACTUAL_LISTENING ){ + that[ '_listeners' ] && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 + that[ '_flags' ] &= ~X_Node_State.ACTUAL_LISTENING; + }; + + // ie5では filter の効いている要素をremove時に破棄して、再度append 時に新規生成する + // ちなみに elm.filters に触ると ie8 でなぜかカラム落ちが発生、、、 + if( X_Node_displayNoneFixForIE5 ){ + if( elm.filters && elm.filters.length ){ + //elm.style.removeAttribute( 'filter' ); + isChild = false; + delete that[ '_rawObject' ]; + // 破棄前にインタラクティブな属性値を控える + if( X_Node_Attr_HAS_VALUE[ that[ '_tag' ] ] && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'value', that[ '_newAttrs' ] ) ) ){ + if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; + that[ '_attrs' ].value = elm.value; + }; + if( that[ '_tag' ] === 'OPTION' && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'selected', that[ '_newAttrs' ] ) ) ){ + if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; + that[ '_attrs' ].selected = elm.selected; + }; + if( that[ '_tag' ] === 'SELECT' && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'selectedIndex', that[ '_newAttrs' ] ) ) ){ + 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' ] ) ) ){ + that[ '_attrs' ].checked = elm.checked; + }; + // 子要素への参照を外す + elm.innerHTML = ''; + }; + }; + + if( !X_UA[ 'MacIE' ] ){ + // elm.parentNode.tagName for ie7 !isChild && elm.parentNode && elm.parentNode.tagName && elm.parentNode.removeChild( elm ); } else { !isChild && elm.parentNode && elm.parentNode.tagName && X_TEMP._fixed_remove( elm, that ); @@ -1393,8 +1905,8 @@ var X_Node__actualRemove = }) : X_UA_DOM.IE4 ? ( function( that, isChild ){ - var xnodes = that._xnodes, - elm = that._rawObject || X_Node__ie4getRawNode( that ), + var xnodes = that[ '_xnodes' ], + elm = that[ '_rawObject' ] || X_Node__ie4getRawNode( that ), i, l, xnode; if( xnodes && ( l = xnodes.length ) ){ for( i = 0; i < l; ++i ){ @@ -1403,17 +1915,32 @@ var X_Node__actualRemove = }; if( !elm ) return; - that._listeners && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 + that[ '_listeners' ] && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 - if( X_Node_Attr_HAS_VALUE[ that._tag ] && ( !that._newAttrs || !X_Object_inObject( 'value', that._newAttrs ) ) ){ - that._attrs.value = elm.value; + // 破棄前にインタラクティブな属性値を控える + if( X_Node_Attr_HAS_VALUE[ that[ '_tag' ] ] && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'value', that[ '_newAttrs' ] ) ) ){ + if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; + that[ '_attrs' ].value = elm.value; + }; + if( that[ '_tag' ] === 'OPTION' && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'selected', that[ '_newAttrs' ] ) ) ){ + if( !that[ '_attrs' ] ) that[ '_attrs' ] = {}; + that[ '_attrs' ].selected = elm.selected; + }; + if( that[ '_tag' ] === 'SELECT' && ( !that[ '_newAttrs' ] || !X_Object_inObject( 'selectedIndex', that[ '_newAttrs' ] ) ) ){ + 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[ '_attrs' ] ) that[ '_attrs' ] = {}; + that[ '_attrs' ].checked = elm.checked; + }; + elm.removeAttribute( 'id' ); // ? - //document.all[ that._id || ( 'ie4uid' + that._uid ) ] = null; // MacIE5 でエラー + //document.all[ that[ '_id' ] || ( 'ie4uid' + that[ '_uid' ] ) ] = null; // MacIE5 でエラー if( !isChild ) elm.outerHTML = ''; - delete that._rawObject; + delete that[ '_rawObject' ]; }) : (function(){}); -X_ViewPort.listenOnce( X.Event.UNLOAD, X_Node__actualRemove, [ X_Node_html, true ] ); +X_ViewPort[ 'listenOnce' ]( X_EVENT_UNLOAD, X_Node__actualRemove, [ X_Node_html, true ] );