X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=0.6.x%2Fjs%2F02_dom%2F02_XNode.js;h=451ea0eab0bccade77f61fab1ecf0f6a3f4df124;hb=36f999435e807f3d04f899b2a84eb1dc31df34b7;hp=da8f553c3d81c00e41adb160b13661b8f0cf651a;hpb=babda23632a3176d9fe546fbab6f79c1fef0e3d4;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 da8f553..451ea0e 100644 --- a/0.6.x/js/02_dom/02_XNode.js +++ b/0.6.x/js/02_dom/02_XNode.js @@ -1,5 +1,6 @@ var - + /** @enum {number} + * @const */ X_Node_State = { DESTROYED : 0x0, EXIST : 0x1, @@ -9,7 +10,7 @@ var 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 : width() のための commitUpdate が不要 + 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 : @@ -18,34 +19,34 @@ var DIRTY_POSITION : 2 << 11, // 要素位置の変更が起こった。 DIRTY_CONTENT : 2 << 12, // width, height, x, y textNode の内容 TODO html と排他なので ID と共通でいい - DIRTY_ID : 2 << 13, // width, height, x, y - DIRTY_CLASSNAME : 2 << 14, // X_Node_CSS_getCharSize, width, height, x, y - DIRTY_ATTR : 2 << 15, // X_Node_CSS_getCharSize, width, height, x, y - DIRTY_CSS : 2 << 16, // X_Node_CSS_getCharSize, width, height, x, y - DIRTY_IE_FILTER : X_UA.IE < 10 && X_UA.ActiveX ? 2 << 17 : 0, // + 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, // - - ELM_NEED_INIT : 2 << 18, - OLD_ATTRTEXT : 2 << 19, - OLD_CSSTEXT : 2 << 20, + ACTUAL_LISTENING : 2 << 17, + OLD_ATTRTEXT : 2 << 18, + OLD_CSSTEXT : 2 << 19, // filter 要素が親子になると不具合が出るのを検出 - IE_FILTER_NOW : 2 << 21, + IE_FILTER_NOW : 2 << 20, //GPU_WAITING : 2 << 20, // 1:子のGPU解除待 - GPU_RESERVED : 2 << 22, // 2:GPU予約 - GPU_NOW : 2 << 23, // 3:GPU now! - GPU_RELEASE_RESERVED : 2 << 24, // 4: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 << 22 : 0, - IE4_HAS_ELEMENT : X_UA.IE4 ? 2 << 23 : 0, - IE4_DIRTY_CHILDREN : X_UA.IE4 ? 2 << 24 : 0, - IE4_FIXED : X_UA.IE4 ? 2 << 25 : 0, + 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 << 25 : 0 - }, + IE5_DISPLAY_NONE_FIX : X_UA[ 'IE5' ] && X_UA[ 'ActiveX' ] ? 2 << 24 : 0 + }; - X_Node_BITMASK_RESET_STYLE = ( ( 2 << 29 ) - 1 + ( 2 << 29 ) ) ^ ( +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 | @@ -57,16 +58,17 @@ var 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_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_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_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, + 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, @@ -80,12 +82,12 @@ 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_documentFragment = document.createDocumentFragment && ( !X_UA[ 'IE' ] || 5.5 <= X_UA[ 'IE' ] ) && document.createDocumentFragment(), // 子の生成後に リアル文書 tree に追加する - X_Node_addTreeAfterChildren = !( X.UA.IE < 9 ), + X_Node_addTreeAfterChildren = !( X_UA[ 'IE' ] < 9 ), X_Node_displayNoneFixForIE5 = !!X_Node_State.IE5_DISPLAY_NONE_FIX, @@ -95,59 +97,178 @@ 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, // X.Class.FINAL + * + * @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, - _flags : X_Node_State.DESTROYED, - - _rawObject : null, - _rect : null, // - _fontSize : 0, + /** + * 要素に振られるユニークID + * @type {number} + * @private + * @alias Node.prototype._uid + */ + '_uid' : 0, - parent : null, // remove された枝も親子構造は維持している。 - _xnodes : null, // Array. - - _tag : '', - _text : '', - _id : '', - _className : '', - - _attrs : null, // see X_Node_Attr - _newAttrs : null, - _attrText : '', + /** + * Node の状態を表すフラグ。 + * @type {number} + * @private + * @alias Node.prototype._flags + */ + '_flags' : X_Node_State.DESTROYED, + + // _rawObject : null, + '_rect' : null, // + + /** + * 最後に計測したフォントサイズを保持している。ツリーが変更されると削除される。 + * @type {number} + * @private + * @alias Node.prototype._fontSize + */ + '_fontSize' : 0, + + /** + * 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' : '', + + /** + * テキストコンテンツ。テキストノードで使用。 + * @type {string} + * @private + * @alias Node.prototype._text + */ + '_text' : '', - _css : null, // see X_Node_CSS - _cssText : '', + /** + * id + * @type {string} + * @private + * @alias Node.prototype._id + */ + '_id' : '', - _anime : null, // Hash see X_Node_Anime + /** + * クラス名。複数のクラスが設定されている場合、スペース区切り。 + * @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._flags |= X_Node_State.EXIST; - 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._flags |= X_Node_State.EXIST; + 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 : @@ -158,23 +279,19 @@ var 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._flags & X_Node_State.IN_TREE ) ){ - this._flags |= X_Node_State.IN_TREE; - }; - this._rawObject = v; - this._flags |= X_Node_State.EXIST; - this._tag = v.tagName.toUpperCase(); - this._id = v.id; - this._className = v.className; + 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 を落とす + this[ 'cssText' ]( v.style.cssText ); + this[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; // X_Node_State.DIRTY_CSS を落とす - // TODO attr の回収は不可能、、、 + // TODO attr の回収は不可能、、、? if( X_UA_DOM.IE4 ){ v.setAttribute( 'UID', '' + uid ); } else { - v.UID = uid; + v[ 'UID' ] = uid; }; // childNodes... break; @@ -182,13 +299,9 @@ var 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._flags & X_Node_State.IN_TREE ) ){ - this._flags |= X_Node_State.IN_TREE; - }; - this._rawObject = v; - this._flags |= X_Node_State.EXIST; - this._text = v.data; - v.UID = uid; + this[ '_rawObject' ] = v; + this[ '_text' ] = v.data; + v[ 'UID' ] = uid; break; case X_Node_TYPE.HTML_STRING : @@ -199,11 +312,18 @@ var 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 ); }, // attr @@ -211,62 +331,54 @@ var // find // animate, stop - create : X_Node_create, + 'create' : X_Node_create, - createAt : X_Node_createAt, + 'createAt' : X_Node_createAt, - createText : X_Node_createText, + 'createText' : X_Node_createText, - createTextAt : X_Node_createTextAt, + 'createTextAt' : X_Node_createTextAt, - clone : X_Node_clone, + 'clone' : X_Node_clone, - append : X_Node_append, + 'append' : X_Node_append, - appendAt : X_Node_appendAt, + 'appendAt' : X_Node_appendAt, - appendTo : X_Node_appendTo, + 'appendTo' : X_Node_appendTo, - appendToRoot : X_Node_appendToRoot, + 'prev' : X_Node_prev, - before : X_Node_before, // remove + 'next' : X_Node_next, - prev : X_Node_before, + 'swap' : X_Node_swap, - after : X_Node_after, // remove + 'remove' : X_Node_remove, - next : X_Node_after, + 'empty' : X_Node_empty, - swap : X_Node_swap, + 'contains' : X_Node_contains, - remove : X_Node_remove, + 'getChildAt' : X_Node_getChildAt, - empty : X_Node_empty, + 'numChildren' : X_Node_numChildren, - destroy : X_Node_destroy, // -> kill && kill event + 'firstChild' : X_Node_firstChild, - contains : X_Node_contains, + 'lastChild' : X_Node_lastChild, - getChildAt : X_Node_getChildAt, + 'getOrder' : X_Node_getOrder, - numChildren : X_Node_length, + 'className' : X_Node_className, + 'addClass' : X_Node_addClass, + 'removeClass' : X_Node_removeClass, + 'toggleClass' : X_Node_toggleClass, + 'hasClass' : X_Node_hasClass, - firstChild : X_Node_firstChild, - - lastChild : X_Node_lastChild, - - getOrder : X_Node_getOrder, - - 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 } ); @@ -278,11 +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.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[ 'instanceOf' ] && v[ 'instanceOf' ]( Node ) ) return X_Node_TYPE.XNODE; return 0; }; function X_Node_getXNode( v ){ @@ -297,33 +410,33 @@ function X_Node_getXNode( v ){ 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 ] @@ -333,15 +446,15 @@ var X_Node_isXmlDocument = * remove : * X_Node_reserveRemoval = [] に追加。commitUpdate で remove * add : - * X_Node_reserveRemoval にいたら消す, new_parent._xnodes に挿入 + * X_Node_reserveRemoval にいたら消す, new_parent[ '_xnodes' ] に挿入 */ X_Node_reserveRemoval = []; 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 ] ) ); + return that[ '_rawObject' ] || + ( that[ '_rawObject' ] = document.all[ 'ie4uid' + that[ '_uid' ] ] ) || + ( that[ '_id' ] && ( that[ '_rawObject' ] = document.all[ that[ '_id' ] ] ) ); }; @@ -349,97 +462,153 @@ 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 ); + 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 ); }; }; -/* -------------------------------------- - * Create +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._tag ) return; - this.append( xnode = X_Doc_create( tag, opt_attrs, opt_css ) ); + 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; - if( !this._tag ) return; - this.appendAt( index, xnode = X_Doc_create( tag, opt_attrs, opt_css ) ); + 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._tag ) return; - this.append( xnode = X_Doc_createText( text ) ); + 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 xnode; - if( !this._tag ) return; - this.appendAt( index, xnode = X_Doc_createText( text ) ); + 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; - if( this._tag ){ + if( this[ '_tag' ] ){ 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 ) ){ + 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 ) ); + xnode[ 'append' ]( xnodes[ i ][ 'clone' ]( true ) ); }; }; return xnode; }; X_Node_newByText = true; - return new Node( this._text ); + 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._tag ) 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 : // 親の xnodes から v を消す - v.parent && v.remove(); + 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; + 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; @@ -447,34 +616,41 @@ function X_Node_append( v ){ v.parent = this; xnodes[ xnodes.length ] = v; - if( this._flags & X_Node_State.IN_TREE ){ - v._flags |= X_Node_State.IN_TREE; - v._xnodes && X_Node_toggleInTreeFlag( v._xnodes, true ); + 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._tag ) 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; }; @@ -482,39 +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 : // 親の xnodes から v を消す - v.parent && v.remove(); + if( v.parent ){ + 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; + 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 ); - if( this._flags & X_Node_State.IN_TREE ){ - v._flags |= X_Node_State.IN_TREE; - v._xnodes && X_Node_toggleInTreeFlag( v._xnodes, true ); + 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 ); @@ -524,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; }; @@ -550,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; }; @@ -575,34 +772,40 @@ 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; }; +/** + * 要素の入れ替え。自身は 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, @@ -611,23 +814,23 @@ function X_Node_remove(){ if( !parent ) return this; delete this.parent; - parent._xnodes.splice( parent._xnodes.indexOf( this ), 1 ); + 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( 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 ); + 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; + if( !this[ '_tag' ] && ( parent[ '_flags' ] & X_Node_State.IE4_FIXED ) === 0 ){ + parent[ '_flags' ] |= X_Node_State.IE4_DIRTY_CHILDREN; }; } else { - elm = this._rawObject; + elm = this[ '_rawObject' ]; if( elm && elm.parentNode && elm.parentNode.tagName ){ X_Node_reserveRemoval[ X_Node_reserveRemoval.length ] = this; X_Node_reserveUpdate(); @@ -637,139 +840,154 @@ function X_Node_remove(){ 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; }; -/* -------------------------------------- - * TODO destory -> kill - */ - -function X_Node_destroy( isChild ){ - var xnodes = this._xnodes, i, elm; - - if( !this._flags ) return; - - elm = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); - - if( xnodes && ( i = xnodes.length ) ){ - //for( ; i; ){ - // xnodes[ --i ].destroy( true ); - //}; - }; - elm && this._listeners && this.unlisten(); // イベントの退避 - - if( this._flags & X_Node_State.IN_TREE ){ - !isChild && this.remove(); - delete this._flags; - } 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 ]; -}; - function X_Node_onBeforeKill( e ){ - var xnodes = this._xnodes, - elm = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ), - i; + var xnodes = this[ '_xnodes' ], i, elm; - if( !this._flags ) return; - - delete X_Node_CHASHE[ this._uid ]; + 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; ){ X_Node_onBeforeKill.call( xnodes[ --i ] ); }; }; - e && this.remove(); - elm && this._listeners && this.unlisten(); // イベントの退避 - - if( e && elm && elm.parentNode && elm.parentNode.tagName ){ - delete this._flags; - return X.Callback.PREVENT_DEFAULT; + 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._tag || this === v ) 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 true; 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._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 _ = ' '; @@ -778,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._flags |= X_Node_State.DIRTY_CLASSNAME; - this._flags & X_Node_State.IN_TREE && 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; @@ -814,82 +1050,108 @@ 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 - * - * outerHTML が欲しい場合は、xnode.call('outerHTML') とできる。 +/** + * 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._tag ) 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._tag ){ - return this._text; + if( !this[ '_tag' ] ){ + return this[ '_text' ]; }; - this._flags & X_Node_State.OLD_CSSTEXT && X_Node_CSS_objToCssText( this ); + 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._flags & X_Node_State.OLD_ATTRTEXT ? X_Node_Attr_objToAttrText( this ) : 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; }; - !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 @@ -897,33 +1159,34 @@ function X_Node_text( text ){ 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(); + 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 ]._tag ){ - 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._tag ){ - 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' ]; }; /* @@ -932,33 +1195,36 @@ function X_Node_text( text ){ function X_Node_call( name /*, opt_args... */ ){ var l = arguments.length - 1, raw, func, args, params, i; - - X_Node_updateTimerID && X_Node_startUpdate(); - + switch( name ){ case 'nodeType' : - return this._tag ? 1 : 3; - case 'text' : - return this.text(); - case 'html' : - case 'innerHTML' : - return this.html(); + return this[ '_tag' ] ? 1 : 3; case 'outerHTML' : X_Node_outerXNode = X_Node_body; // == true ならなんでもよい。型を合わすために xbody にしている - v = this.html(); + 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 ) ); + return !!( this[ '_flags' ] & ( X_Node_State.GPU_NOW | X_Node_State.GPU_RELEASE_RESERVED ) ); }; - raw = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this ); + 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; + return; + }; + func = raw[ name ]; - if( X.Type.isFunction( func ) ){ + if( X_Type_isFunction( func ) ){ if( l ){ args = X_Object_cloneArray( arguments ); args.shift(); @@ -966,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 ){ @@ -1011,7 +1277,7 @@ function X_Node_each( func /*, opt_args */ ){ */ 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; @@ -1019,16 +1285,16 @@ var X_Node_updateReservedByReleaseGPU = false; function X_Node_startUpdate( time ){ 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; @@ -1036,17 +1302,18 @@ function X_Node_startUpdate( time ){ if( i = removal.length ){ for( ; i; ){ xnode = removal[ --i ]; + // TODO GPU レイヤーの子の場合、remove をスキップする。 非GPU レイヤーへ apppend される場合、clone する? X_Node__actualRemove( xnode ); - !xnode._flags && xnode.kill(); + ( xnode[ '_flags' ] & X_Node_State.EXIST ) === 0 && xnode[ 'kill' ](); }; removal.length = 0; }; - 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 ); + 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 ); + 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 ){ @@ -1056,15 +1323,17 @@ function X_Node_startUpdate( time ){ 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 ); + X_ViewPort[ '_listeners' ] && X_ViewPort[ '_listeners' ][ X_EVENT_AFTER_UPDATE ] && X_ViewPort[ 'asyncDispatch' ]( X_EVENT_AFTER_UPDATE ); }; /* - * 1. GPU_NOW の場合、これ以下の一切の更新を行わない - * 2. GPU解放予約 の場合、この要素のみ変更を行う。rAF 後にさらに更新するためフラグを立てる。 + * 1. GPU_NOW の場合、子の変更は行わない + * 2. GPU解放予約 の場合//、この要素のみ変更を行う。rAF 後にさらに更新するためフラグを立てる。 * 3. GPU予約 -> GPU * 4. style="display:none" の場合、これ以下の変更を行わない。 * 5. ie5 非表示フラグが立っていて、親と自身の class・id によって非表示になっていて、親と自身に変更がない。accumulatedFlags を使用。 @@ -1078,34 +1347,33 @@ function X_Node_startUpdate( time ){ var X_Node__commitUpdate = X_UA_DOM.W3C ? ( function( that, parentElement, nextElement, accumulatedFlags ){ - var elm = that._rawObject, + 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 ); + 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; }; // 2. GPU解放予約 - // TODO もしかしたらこのタイミングで更新できるかも。 - if( that._flags & X_Node_State.GPU_RELEASE_RESERVED ){ - console.log( '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; + that[ '_flags' ] &= X_Node_BitMask_RESET_GPU; + //return elm;// TODO もしかしたらこのタイミングで更新できるかも。 }; // 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; + 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( that[ '_flags' ] & X_Node_State.STYLE_IS_DISPLAY_NONE ){ if( X_Node_displayNoneFixForIE5 ){ // filter の効いている要素を含む要素は display:none が無視される。 // filter = '' で削除はできるが、再表示時に filter が消える。 -> filter な要素を削除してしまう。 @@ -1115,13 +1383,13 @@ var X_Node__commitUpdate = return nextElement; }; elm && ( elm.style.display = 'none' ); - return elm || nextElement; - }; + return ( elm && elm.nextSibling === nextElement ) ? elm : nextElement; + }; // 5. ie5 非表示fixフラグ - accumulatedFlags |= that._flags; + accumulatedFlags |= that[ '_flags' ]; - if( that._flags & X_Node_State.IE5_DISPLAY_NONE_FIX ){ + 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; }; @@ -1129,24 +1397,27 @@ var X_Node__commitUpdate = // 6. 要素の生成 if( !elm ){ - if( !that._tag ){ - that._flags &= X_Node_BitMask_RESET_DIRTY; - that._rawObject = elm = document.createTextNode( X_String_chrReferanceTo( that._text ) ); + 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.DIRTY_CSS && X_Node_CSS_objToCssText( that, true ); + that[ '_flags' ] & X_Node_State.OLD_CSSTEXT && X_Node_CSS_objToCssText( that, true ); // OLD_CSSTEXT ?? - that._rawObject = elm = + that[ '_rawObject' ] = elm = document.createElement( [ - '<', that._tag, - ' UID="', that._uid, '"', - that._id ? ' id="' + that._id + '"' : '', - that._className ? ' class="' + that._className + '"' : '', - that._flags & X_Node_State.OLD_ATTRTEXT ? X_Node_Attr_objToAttrText( that ) : that._attrText, - that._cssText ? ' style="' + that._cssText + '"' : '', - '>' ].join( '' ) ); + '<', 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 ); + that[ '_rawObject' ] = elm = document.createElement( that[ '_tag' ] ); }; // IE には要素追加のタイミングで起こるメモリリークがありここで追加 @@ -1154,33 +1425,34 @@ var X_Node__commitUpdate = nextElement ? parentElement.insertBefore( elm, nextElement ) : parentElement.appendChild( elm ); - //elm.UID = that._uid; - // src の onload があるので先ではないか? - // TODO ie の str から要素を作る場合、srcだけ イベント設定後ではないか? - X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 }; - if( that._tag ){ + 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 ); + // 連続する要素の差し替えの場合に有効 }; if( X_Node_strictElmCreation ){ - // TODO src 等の設定 - delete that._newAttrs; - that._flags &= X_Node_BitMask_RESET_DIRTY; - that._flags |= X_Node_State.DIRTY_IE_FILTER;// doc 追加後に filter を指定しないと有効にならない。 + 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_State.DIRTY_ID | X_Node_State.DIRTY_CLASSNAME | X_Node_State.DIRTY_ATTR | X_Node_State.DIRTY_CSS | X_Node_State.DIRTY_IE_FILTER; + 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[ 'src' ] ){ + 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' ); + that[ 'attr' ]( 'src', 'about:blank' ); }; }; }; @@ -1193,35 +1465,35 @@ var X_Node__commitUpdate = nextElement ? parentElement.insertBefore( elm, nextElement ) : parentElement.appendChild( elm ); - - if( X_Node_displayNoneFixForIE5 ) that._flags |= X_Node_State.DIRTY_POSITION; }; - that._flags & X_Node_State.DIRTY_POSITION && X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 - that._flags &= ~X_Node_State.DIRTY_POSITION; + 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; - - that._flags & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); - - + if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ){ + delete that[ '_fontSize' ]; + X_Node__updateRawNode( that, elm ); + }; + // 9. ie5 only // 親及び自身へのクラス・id指定で display : none になるケースがありそれを検出 // 生成と破棄が繰り返されてしまう、親と自身の id, class が変わった場合だけ再生成。 accumulatedFlags & ( ID | CLASSNAME ) // currentStyle を観ていたときはエラーで停止する、alert と挟むと正常に動いて支離滅裂 - if( X_Node_displayNoneFixForIE5 && that._tag ){ + if( X_Node_displayNoneFixForIE5 && that[ '_tag' ] ){ if( elm.runtimeStyle.display === 'none' ){ X_Node__actualRemove( that ); - that._flags |= X_Node_State.IE5_DISPLAY_NONE_FIX; + that[ '_flags' ] |= X_Node_State.IE5_DISPLAY_NONE_FIX; return nextElement; } else { - that._flags &= ~X_Node_State.IE5_DISPLAY_NONE_FIX; + that[ '_flags' ] &= ~X_Node_State.IE5_DISPLAY_NONE_FIX; }; }; // 10. 子要素の更新。 - if( ( xnodes = that._xnodes ) && ( l = xnodes.length ) ) { + if( ( xnodes = that[ '_xnodes' ] ) && ( l = xnodes.length ) ) { for( ; l; ){ next = X_Node__commitUpdate( xnodes[ --l ], elm, next, accumulatedFlags ); }; @@ -1232,23 +1504,26 @@ var X_Node__commitUpdate = parentElement.insertBefore( elm, nextElement ) : parentElement.appendChild( elm ); - X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 + 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, accumulatedFlags ){ - var elm = that._rawObject || X_Node__ie4getRawNode( that ), + 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 ); + 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( 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 する。 @@ -1264,14 +1539,14 @@ 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 ); + return that[ '_rawObject' ] || X_Node__ie4getRawNode( that ); }; - accumulatedFlags |= that._flags; + accumulatedFlags |= that[ '_flags' ]; - xnodes = that._xnodes; + xnodes = that[ '_xnodes' ]; l = xnodes ? xnodes.length : 0; - dirty = !!( that._flags & X_Node_State.IE4_DIRTY_CHILDREN ); + dirty = !!( that[ '_flags' ] & X_Node_State.IE4_DIRTY_CHILDREN ); /* * HTML の下に TextNode だけ 。MIX_FIXED でない場合、削除、追加 を親に通知 @@ -1280,21 +1555,21 @@ var X_Node__commitUpdate = * HTML の下は MIX_FIXED -> TextNode を に置き換えてあるのでW3C DON 的に触ることができる */ if( dirty ){ - that._flags &= ~X_Node_State.IE4_DIRTY_CHILDREN; + 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; + if( xnodes[ i ][ '_tag' ] ){ + that[ '_flags' ] |= X_Node_State.IE4_HAS_ELEMENT; } else { - that._flags |= X_Node_State.IE4_HAS_TEXTNODE; + that[ '_flags' ] |= X_Node_State.IE4_HAS_TEXTNODE; }; - if( that._flags & X_Node_BitMask_IE4_IS_MIX === X_Node_BitMask_IE4_IS_MIX ){ + 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 ){ + 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 ); }; @@ -1308,30 +1583,29 @@ var X_Node__commitUpdate = for( i = 0; i < l; ++i ){ X_Node__afterActualCreate( xnodes[ i ] ); }; - that._flags |= X_Node_State.IE4_FIXED; + that[ '_flags' ] |= X_Node_State.IE4_FIXED; } else - if( that._flags & X_Node_State.IE4_HAS_TEXTNODE ){ + if( that[ '_flags' ] & X_Node_State.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; + if( text[ '_flags' ] & X_Node_BitMask_IS_DIRTY ){ + text[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; dirty = true; }; }; - if( dirty ) elm.innerHTML = that.text(); + if( dirty ) elm.innerHTML = that[ 'text' ](); }; - if( accumulatedFlags & X_Node_BitMask_IS_DIRTY ) delete that._fontSize; + 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 ); + that[ '_flags' ] &= ~X_Node_State.DIRTY_POSITION; + that[ '_flags' ] & X_Node_BitMask_IS_DIRTY && X_Node__updateRawNode( that, elm ); return elm; }) : (function(){}); /* - * TODO IE5 は filter-fix があるため、親から変更を適用し、自信の display:none を調べる * GPU レイヤーするブラウザは、子要素から変更を当てていく? <- とりあえず、親要素から。 */ var X_Node__updateRawNode = @@ -1340,31 +1614,31 @@ var X_Node__updateRawNode = var attrs, rename, k, v; // textNode - if( !that._tag ){ - elm.data = X_String_chrReferanceTo( that._text ); - that._flags &= X_Node_BitMask_RESET_DIRTY; + if( !that[ '_tag' ] ){ + elm.data = X_String_chrReferanceTo( that[ '_text' ] ); + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; return; }; // id - if( that._flags & X_Node_State.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._flags & X_Node_State.DIRTY_CLASSNAME ){ - that._className ? ( elm.className = that._className ) : ( elm.className && elm.removeAttribute( X_UA.IE5678 ? 'className' : 'class' ) ); // className は ie7-? + 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 ) ){ + 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 ){ + switch( that[ '_tag' ] + k ){ case 'TEXTAREAvalue' : // IETester 5.5 ではエラーが出なかった.MultipulIE5.5 ではエラーが出たので - if( !X_UA.MacIE && X_UA.IE5x ){ + if( !X_UA[ 'MacIE' ] && X_UA[ 'IE5x' ] ){ elm.firstChild ? ( elm.firstChild.data = v || '' ) : elm.appendChild( document.createTextNode( v || '' ) ); @@ -1375,7 +1649,7 @@ var X_Node__updateRawNode = case 'IFRAMEsrc' : // http://outcloud.blogspot.jp/2010/09/iframe.html // この問題は firefox3.6 で確認 - if( X_UA.Gecko ){ + if( X_UA[ 'Gecko' ] && elm.contentWindow ){ elm.contentWindow.location.replace = elm.src = v || ''; continue; }; @@ -1385,7 +1659,7 @@ var X_Node__updateRawNode = // http://d.hatena.ne.jp/NeoCat/20080921/1221940658 // こちらに名前をsetしないとtargetが動作しない // これってあとから name を変更できないバグでは? itozyun - // if( X_UA.IE ) elm.name = elm.contentWindow.name = v || ''; + // if( X_UA[ 'IE' ] ) elm.name = elm.contentWindow.name = v || ''; }; //if( X_EMPTY_OBJECT[ k ] ) continue; @@ -1394,42 +1668,41 @@ var X_Node__updateRawNode = elm.removeAttribute( rename[ k ] || k ) : ( elm[ rename[ k ] || k ] = X_Node_Attr_noValue[ k ] ? k : v ); }; - delete that._newAttrs; + delete that[ '_newAttrs' ]; }; // style - 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 ); + 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; }; } else - if( that._flags & X_Node_State.DIRTY_IE_FILTER ){ + 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; + that[ '_flags' ] |= X_Node_State.IE_FILTER_NOW; } else { elm.style.removeAttribute( 'filter' ); - that._flags &= ~X_Node_State.IE_FILTER_NOW; + that[ '_flags' ] &= ~X_Node_State.IE_FILTER_NOW; }; }; - that._flags &= X_Node_BitMask_RESET_DIRTY; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }) : X_UA_DOM.IE4 ? ( function( that, elm ){ var attrs, rename, k, v; // fake textNode - if( !that._tag ){ - elm.innerText = that._text; - that._flags &= X_Node_BitMask_RESET_DIRTY; + if( !that[ '_tag' ] ){ + elm.innerText = that[ '_text' ]; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; return; }; @@ -1438,35 +1711,34 @@ var X_Node__updateRawNode = * title、className、id、lang、language には setAttribute でなく、element.id で直接読み書きできる */ // id - if( that._flags & X_Node_State.DIRTY_ID ) elm.setAttribute( 'id', that._id || ( 'ie4uid' + that._uid ) ); + if( that[ '_flags' ] & X_Node_State.DIRTY_ID ) elm.setAttribute( 'id', that[ '_id' ] || ( 'ie4uid' + that[ '_uid' ] ) ); // className - if( that._flags & X_Node_State.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._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; + 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; }; } else - if( that._flags & X_Node_State.DIRTY_IE_FILTER ){ + 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; + that[ '_flags' ] |= X_Node_State.IE_FILTER_NOW; } else { elm.style.removeAttribute( 'filter' ); - that._flags &= ~X_Node_State.IE_FILTER_NOW; + that[ '_flags' ] &= ~X_Node_State.IE_FILTER_NOW; }; }; // attr - if( that._flags & X_Node_State.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; @@ -1474,10 +1746,10 @@ var X_Node__updateRawNode = elm.removeAttribute( rename[ k ] || k ) : elm.setAttribute( rename[ k ] || k, X_Node_Attr_noValue[ k ] ? k : v ); }; - delete that._newAttrs; + delete that[ '_newAttrs' ]; }; - that._flags &= X_Node_BitMask_RESET_DIRTY; + that[ '_flags' ] &= X_Node_BitMask_RESET_DIRTY; }) : (function(){}); @@ -1500,54 +1772,54 @@ var X_Node__updateRawNode = */ var X_Node__actualCreate = X_UA_DOM.IE4 && (function( that, isChild ){ - var uid = that._uid, + var uid = that[ '_uid' ], html, xnodes, n, i, l; - if( !that._tag ){ - html = [ '', that._text, '' ];// fake textNode - delete that._rawObject; + if( !that[ '_tag' ] ){ + html = [ '', that[ '_text' ], '' ];// fake textNode + delete that[ '_rawObject' ]; } else { if( !isChild ) X_Node__actualRemove( that, /* true */ false ); - that._flags & X_Node_State.DIRTY_CSS && X_Node_CSS_objToCssText( that, true ); + 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._flags & X_Node_State.OLD_ATTRTEXT ? X_Node_Attr_objToAttrText( that ) : 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( ( xnodes = that[ '_xnodes' ] ) && ( l = xnodes.length ) ){ - that._flags &= ~X_Node_State.IE4_DIRTY_CHILDREN; + 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; + if( xnodes[ i ][ '_tag' ] ){ + that[ '_flags' ] |= X_Node_State.IE4_HAS_ELEMENT; } else { - that._flags |= X_Node_State.IE4_HAS_TEXTNODE; + that[ '_flags' ] |= X_Node_State.IE4_HAS_TEXTNODE; }; - if( that._flags & X_Node_BitMask_IE4_IS_MIX === X_Node_BitMask_IE4_IS_MIX ){ + 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 ){ + if( that[ '_flags' ] & X_Node_BitMask_IE4_IS_MIX === X_Node_State.IE4_HAS_TEXTNODE ){ // only textnode - html[ n ] = that.text(); + html[ n ] = that[ 'text' ](); ++n; } else { for( i = 0; i < l; ++i ){ html[ n ] = X_Node__actualCreate( xnodes[ i ], true ); ++n; }; - that._flags |= X_Node_State.IE4_FIXED; + 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( '' ); @@ -1557,15 +1829,19 @@ var X_Node__afterActualCreate = X_UA_DOM.IE4 && (function( that ){ var xnodes, i, v; - if( !that._tag ) return that; + if( !that[ '_tag' ] ) return that; - if( ( xnodes = that._xnodes ) && ( i = xnodes.length ) ){ + if( ( xnodes = that[ '_xnodes' ] ) && ( i = xnodes.length ) ){ for( ; i; ){ X_Node__afterActualCreate( xnodes[ --i ] ); }; }; - that._flags & X_Node_State.DIRTY_IE_FILTER && X_Node__updateRawNode( that, that._rawObject || X_Node__ie4getRawNode( that ) ); - that._flags &= X_Node_BitMask_RESET_DIRTY; + // ネットワーク系属性と 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; + }; X_EventDispatcher_toggleAllEvents( that, true );// イベントの復帰 }); @@ -1574,20 +1850,23 @@ var X_Node__actualRemove = // 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._tag && X_Node__actualRemove( child, true ); + child[ '_tag' ] && X_Node__actualRemove( child, true ); }; }; if( !elm ) return; - that._listeners && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 + 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 でなぜかカラム落ちが発生、、、 @@ -1595,16 +1874,29 @@ var X_Node__actualRemove = if( elm.filters && elm.filters.length ){ //elm.style.removeAttribute( 'filter' ); isChild = false; - delete that._rawObject; - // 破棄前に value を控える TODO checked, selected も! - if( X_Node_Attr_HAS_VALUE[ that._tag ] && ( !that._newAttrs || !X_Object_inObject( 'value', that._newAttrs ) ) ){ - that._attrs.value = elm.value; + 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 ){ + if( !X_UA[ 'MacIE' ] ){ // elm.parentNode.tagName for ie7 !isChild && elm.parentNode && elm.parentNode.tagName && elm.parentNode.removeChild( elm ); } else { @@ -1613,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 ){ @@ -1623,18 +1915,32 @@ var X_Node__actualRemove = }; if( !elm ) return; - that._listeners && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 + that[ '_listeners' ] && X_EventDispatcher_toggleAllEvents( that, false );// イベントの退避 - // 破棄前に value を控える TODO checked, selected も! - 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 ] );