OSDN Git Service

Version 0.6.41, fix for Opera8 & NN7.2+.
[pettanr/clientJs.git] / 0.6.x / js / dom / 19_XDomParser.js
index 27845ba..db0c897 100644 (file)
-/*
- * Original code by Erik John Resig (ejohn.org)
- * http://ejohn.org/blog/pure-javascript-html-parser/
- *
- */
-
-(function(){
-
-       var alphabets  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
-               whiteSpace = '\t\r\n\f\b ';
-
-       // Empty Elements - HTML 4.01
-       var empty = X.Dom.DTD.EMPTY;
-
-       // Block Elements - HTML 4.01
-       var block = {address:1,applet:1,blockquote:1,button:1,center:1,dd:1,del:1,dir:1,div:1,dl:1,dt:1,fieldset:1,form:1,frameset:1,hr:1,iframe:1,ins:1,isindex:1,li:1,map:1,menu:1,noframes:1,noscript:1,object:1,ol:1,p:1,pre:1,script:1,table:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,ul:1};
-
-       // Inline Elements - HTML 4.01
-       var inline = {a:1,abbr:1,acronym:1,applet:1,b:1,basefont:1,bdo:1,big:1,br:1,button:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,iframe:1,img:1,input:1,ins:1,kbd:1,label:1,map:1,object:1,q:1,s:1,samp:1,script:1,select:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,textarea:1,tt:1,u:1,'var':1};
-
-       // Elements that you can, intentionally, leave open
-       // (and which close themselves)
-       var closeSelf = {colgroup:1,dd:1,dt:1,li:1,options:1,p:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1}; // add tbody
-
-       // todo:
-       var plainText = { plaintext : 1, xmp : 1 };
-
-       var sisters = {
-               th : { td : 1 },
-               td : { th : 1 },
-               dt : { dd : 1 },
-               dd : { dt : 1 },
-               colgroup : { caption : 1 },
-               thead    : { caption : 1, colgroup : 1 },
-               tfoot    : { caption : 1, colgroup : 1, thead : 1, tbody : 1 },
-               tbody    : { caption : 1, colgroup : 1, thead : 1, tfoot : 1 }
-       };
-       /*
-        * http://www.tohoho-web.com/html/tbody.htm
-        * HTML4.01では、ヘッダとフッタを先読みして表示するために、<tbody> よりも <tfoot> の方を先に記述しなくてはならないと定義されています。
-        * IE5.0 などでは HEAD → BODY → FOOT の順に表示するのですが、
-        * <tfoot> に未対応の古いブラウザでは、HEAD → FOOT → BODY の順に表示されてしまいます。
-        * また、HTML5 では、<tfoot> と <tbody> の順番はどちらでもよいことになりました。
-        */
-
-       // Attributes that have their values filled in disabled="disabled"
-       var fillAttrs = X.Dom.Attr.noValue; //{checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};
-
-       // Special Elements (can contain anything)
-       var special = {script:1,style:1};
-
-       X.Dom.Parser = function( html, handler ) {
-               var stack    = [],
-                       lastHtml = html,
-                       chars, last, text, index;
-
-               while ( html ) {
-                       chars = true;
-                       last  = stack[ stack.length - 1 ];
-                       
-                       // Make sure we're not in a script or style element
-                       if ( last && special[ last.toLowerCase() ] === 1 ) {
-                               if( 0 <= ( index = _parseEndTag( stack, handler, html ) ) ){
-                                       //handler.chars( html.substring( 0, index ) );
-                                       html = html.substring( index );
-                               };
-                       } else {
-                               // Comment
-                               if ( html.indexOf("<!--") === 0 ) {
-                                       if ( 0 < ( index = html.indexOf("-->") ) ) {
-                                               handler.comment( html.substring( 4, index ) );
-                                               html = html.substring( index + 3 );
-                                               chars = false;
-                                       };
-       
-                               // end tag
-                               } else if ( html.indexOf("</") === 0 ) {
-                                       if ( 2 < ( index = _parseEndTag( stack, handler, html ) ) ) {
-                                               html = html.substring( index );
-                                               chars = false;
-                                       };
-       
-                               // start tag
-                               } else if ( html.indexOf("<") === 0 ) {
-                                       if( index = _parseStartTag( stack, last, handler, html ) ){
-                                               html  = html.substring( index );
-                                               chars = false;
-                                       } else
-                                       if( index === false ){
-                                               return;
-                                       };
-                               };
-
-                               if ( chars ) {
-                                       index = html.indexOf("<");
-                                       
-                                       text = index < 0 ? html : html.substring( 0, index );
-                                       html = index < 0 ? '' : html.substring( index );
-                                       
-                                       handler.chars( text );
-                               };
-
-                       };
-
-                       if ( html === lastHtml ){
-                               handler.err( html );
-                               return;
-                       };
-                       lastHtml = html;
-               };
-               
-               // Clean up any remaining tags
-               parseEndTag( stack, handler );
-       };
-
-       function _parseStartTag( stack, last, handler, html ){
-               var phase = 0,
-                       l     = html.length,
-                       i     = 0,
-                       attrs = [],
-                       tagName, empty,
-                       chr, start, attrName, quot, escape;
-               
-               while( i < l && phase < 9 ){
-                       chr = html.charAt( i );
-                       switch( phase ){
-                               case 0 :
-                                       chr === '<' && ( ++phase );
-                                       break;
-                               case 1 : // タグ名の開始を待つ
-                                       alphabets.indexOf( chr ) !== -1 && ( ++phase && ( start = i ) );
-                                       break;
-                               case 2 : // タグ名の終わりの空白文字を待つ
-                                       whiteSpace.indexOf( chr ) !== -1 ?
-                                               ( ++phase && ( tagName = html.substring( start, i ) ) ) :
-                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&
-                                               ( ( tagName = html.substring( start, i ) ) && ( phase = 9 ) );
-                                       break;
-                               case 3 : // 属性名の開始を待つ
-                                       alphabets.indexOf( chr ) !== -1 ?
-                                               ( ++phase && ( start = i ) ) :
-                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&
-                                               ( phase = 9 );
-                                       break;
-                               case 4 : // 属性名の終わりを待つ
-                                       chr === '=' ?
-                                               ( ( phase = 6 ) && ( attrName = html.substring( start, i ) ) ) :
-                                       whiteSpace.indexOf( chr ) !== -1 &&
-                                               ( ( phase = 5 ) && ( attrName = html.substring( start, i ) ) );
-                                       break;
-                               case 5 : // 属性の = または次の属性または htmlタグの閉じ
-                                       alphabets.indexOf( chr ) !== -1 ?
-                                               ( ( phase = 4 ) && ( attrs[ attrs.length ] = attrName ) && ( start = i ) ) :
-                                       chr === '=' ?
-                                               ( phase = 6 ) :
-                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&
-                                               ( ( phase = 9 ) && ( attrs[ attrs.length ] = attrName ) );
-                                       break;
-                               case 6 : // 属性値の開始 quot を待つ
-                                       ( chr === '"' || chr === "'" ) ?
-                                               ( ( phase = 7 ) && ( quot = chr ) && ( start = i + 1 ) ):
-                                       whiteSpace.indexOf( chr ) === -1 &&
-                                               ( ( phase = 8 ) && ( start = i ) ); // no quot
-                                       break;
-                               case 7 : //属性値の閉じ quot を待つ
-                                       !escape && ( chr === quot ) && ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) );
-                                       break;
-                               case 8 : //閉じ quot のない属性の値
-                                       whiteSpace.indexOf( chr ) !== -1 ?
-                                               ( ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) ) :
-                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&
-                                               ( ( phase = 9 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) );
-                                       break;
-                       };
-                       escape = chr === '\\' && !escape; // \\\\ is not escape for "
-                       ++i;
-               };
-               if( phase === 9 ){
-                       if( parseStartTag( stack, last, handler, tagName, attrs, empty, i ) === false ) return false;
-                       return i;
-               };
-               return 0; // error
-       };
-
-       function _parseEndTag( stack, handler, html ){
-               var phase = 0,
-                       l     = html.length,
-                       i     = 0,
-                       tagName,
-                       chr, start;
-               
-               while( i < l && phase < 9 ){
-                       chr = html.charAt( i );
-                       switch( phase ){
-                               case 0 :
-                                       html.substr( i, 2 ) === '</' && ( ++phase && ++i );
-                                       break;
-                               case 1 : // タグ名の開始を待つ
-                                       alphabets.indexOf( chr ) !== -1 && ( ++phase && ( start = i ) );
-                                       break;
-                               case 2 : // タグ名の終わりの空白文字を待つ
-                                       whiteSpace.indexOf( chr ) !== -1 && ( ++phase );
-                                       ( chr === '>' ) && ( phase = 9 );
-                                       ( phase !== 2 ) && ( tagName = html.substring( start, i ) );
-                                       break;
-                               case 3 : // 属性名の開始を待つ
-                                       chr === '>' && ( phase = 9 );
-                                       break;
-                       };
-                       ++i;
-               };
-               if( phase === 9 ){
-                       parseEndTag( stack, handler, tagName );
-                       return i;
-               };
-               return 0; // error
-       };
-
-       function saveAttr( attrs, name, value ){
-               name  = name.toLowerCase();
-               value = fillAttrs[ name ] === 1 ? name : value;
-               attrs[ attrs.length ] = {
-                       name    : name,
-                       value   : value,
-                       escaped :
-                               value.indexOf( '"' ) !== -1 ?
-                                       value.split( '"' ).join( '\\"' ).split( '\\\\"' ).join( '\\"' ) :
-                                       value
-               };
-       };
-
-       function parseStartTag( stack, last, handler, tagName, attrs, unary, index ) {
-               var tagLower = tagName.toLowerCase();
-               if ( block[ tagLower ] === 1 ) {
-                       while ( last && inline[ last.toLowerCase() ] === 1 ) {
-                               parseEndTag( stack, handler, last );
-                               last = stack[ stack.length - 1 ];
-                       };
-               };
-               closeSelf[ tagLower ] === 1 && ( last === tagName || ( sisters[ tagLower ] && sisters[ tagLower ][ last.toLowerCase() ] === 1 ) ) && parseEndTag( stack, handler, last );
-               unary = empty[ tagLower ] === 1 || !!unary;
-               !unary && ( stack[ stack.length ] = tagName );
-               
-               return handler.start( tagName, attrs, unary, index );
-       };
-
-       function parseEndTag( stack, handler, tagName ) {
-               var pos = 0, i = stack.length;
-               // If no tag name is provided, clean shop
-               
-               // Find the closest opened tag of the same type
-               if ( tagName )
-                       for ( pos = i; 0 <= pos; )
-                               if ( stack[ --pos ] === tagName )
-                                       break;
-               
-               if ( 0 <= pos ) {
-                       // Close all the open elements, up the stack
-                       for ( ; pos < i; )
-                               handler.end( stack[ --i ] );
-                       
-                       // Remove the open elements from the stack
-                       stack.length = pos;
-               };
-       };
-
-})();
-
-X.Dom._htmlStringToXNode = {
-       flat : null,
-       nest : [],
-       err : function( html ){
-               this.flat.length = 0;
-               this.ignoreError !== true && X.Notification.warn( 'X.Dom.Parser() error ' + html );
-       },
-       start : function( tagName, attrs, noChild, length ){
-               var xnode,
-                       nest   = this.nest,
-                       flat   = this.flat,
-                       l      = nest.length,
-                       attr, name, i, _attrs; //, toIndex;
-               if( l ){
-                       xnode = nest[ l - 1 ].create( tagName );
-               } else {
-                       xnode = flat[ flat.length ] = X.Dom.Node.create( tagName );
-               };
-               if( !noChild ) nest[ l ] = xnode;
-               if( i = attrs.length ){
-                       _attrs = {};
-                       for( ; i; ){
-                               if( attr = attrs[ --i ] ){
-                                       if( typeof attr === 'string' ){
-                                               name = attr;
-                                               _attrs[ name ] = true;
-                                       } else {
-                                               name = attr.name;
-                                               _attrs[ name ] = attr.escaped;
-                                       };
-                               };
-                       };
-                       xnode.attr( _attrs );
-               };
-       },
-       end : function(){
-               0 < this.nest.length && ( --this.nest.length );
-       },
-       chars : function( text ){
-               if( this.nest.length ){
-                       this.nest[ this.nest.length - 1 ].createText( text );
-               } else {
-                       this.flat[ this.flat.length ] = X.Dom.Node.createText( text );
-               };
-       },
-       comment : X.emptyFunction
-};
-
-X.Dom.parse = function( html, ignoreError ){
-       var worker = X.Dom._htmlStringToXNode, ret;
-       worker.flat = [];
-       worker.nest.length = 0;
-       worker.ignoreError = ignoreError;
-       X.Dom.Parser( html, worker );
-       ret = worker.flat;
-       delete worker.flat;
-       return ret;
-};
-
+\r
+/*\r
+ * Original code by Erik John Resig (ejohn.org)\r
+ * http://ejohn.org/blog/pure-javascript-html-parser/\r
+ *\r
+ */\r
+\r
+X.Dom.Parser = {\r
+       alphabets  : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',\r
+       whiteSpace : '\t\r\n\f\b ',\r
+\r
+       // Empty Elements - HTML 4.01\r
+       empty : X.Dom.DTD.EMPTY,\r
+\r
+       // Block Elements - HTML 4.01\r
+       block : {address:1,applet:1,blockquote:1,button:1,center:1,dd:1,del:1,dir:1,div:1,dl:1,dt:1,fieldset:1,form:1,frameset:1,hr:1,iframe:1,ins:1,isindex:1,li:1,map:1,menu:1,noframes:1,noscript:1,object:1,ol:1,p:1,pre:1,script:1,table:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,ul:1},\r
+\r
+       // Inline Elements - HTML 4.01\r
+       inline : {a:1,abbr:1,acronym:1,applet:1,b:1,basefont:1,bdo:1,big:1,br:1,button:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,iframe:1,img:1,input:1,ins:1,kbd:1,label:1,map:1,object:1,q:1,s:1,samp:1,script:1,select:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,textarea:1,tt:1,u:1,'var':1},\r
+\r
+       // Elements that you can, intentionally, leave open\r
+       // (and which close themselves)\r
+       closeSelf : {colgroup:1,dd:1,dt:1,li:1,options:1,p:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1}, // add tbody\r
+\r
+       sisters : {\r
+               th : { td : 1 },\r
+               td : { th : 1 },\r
+               dt : { dd : 1 },\r
+               dd : { dt : 1 },\r
+               colgroup : { caption : 1 },\r
+               thead    : { caption : 1, colgroup : 1 },\r
+               tfoot    : { caption : 1, colgroup : 1, thead : 1, tbody : 1 },\r
+               tbody    : { caption : 1, colgroup : 1, thead : 1, tfoot : 1 }\r
+       },\r
+       /*\r
+        * http://www.tohoho-web.com/html/tbody.htm\r
+        * HTML4.01では、ヘッダとフッタを先読みして表示するために、<tbody> よりも <tfoot> の方を先に記述しなくてはならないと定義されています。\r
+        * IE5.0 などでは HEAD → BODY → FOOT の順に表示するのですが、\r
+        * <tfoot> に未対応の古いブラウザでは、HEAD → FOOT → BODY の順に表示されてしまいます。\r
+        * また、HTML5 では、<tfoot> と <tbody> の順番はどちらでもよいことになりました。\r
+        */\r
+\r
+       // Attributes that have their values filled in disabled="disabled"\r
+       fillAttrs : X.Dom.Attr.noValue, //{checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};\r
+\r
+       // Special Elements (can contain anything)\r
+       special : { script : 1, style : 1, plaintext : 1, xmp : 1, textarea : 1 },\r
+       \r
+       exec : function( html, handler, async ){\r
+               var special        = X.Dom.Parser.special,\r
+                       plainText      = X.Dom.Parser.plainText,\r
+                       startTime      = async && X.getTime(),\r
+                       _parseStartTag = X.Dom.Parser._parseStartTag,\r
+                       _parseEndTag   = X.Dom.Parser._parseEndTag,\r
+                       stack          = async ? async[ 1 ] : [],\r
+                       lastHtml       = html,\r
+                       chars, last, text, index;\r
+\r
+               while ( html ) {\r
+                       chars = true;\r
+                       last  = stack[ stack.length - 1 ];\r
+                       \r
+                       // Make sure we're not in a script or style element\r
+                       if ( last && special[ last.toLowerCase() ] === 1 ) {\r
+                               if( 0 <= ( index = html.toLowerCase().indexOf( '</' + last.toLowerCase() ) ) ){\r
+                                       handler.chars( html.substring( 0, index ) );\r
+                                       if( index = _parseEndTag( stack, handler, html ) ){\r
+                                               html = html.substring( index );\r
+                                       } else {\r
+                                               handler.chars( html );\r
+                                               html = '';\r
+                                       };\r
+                               } else {\r
+                                       handler.chars( html );\r
+                                       html = '';\r
+                               };\r
+                       } else {\r
+                               // Comment\r
+                               if ( html.indexOf("<!--") === 0 ) {\r
+                                       if ( 0 < ( index = html.indexOf("-->") ) ) {\r
+                                               handler.comment( html.substring( 4, index ) );\r
+                                               html = html.substring( index + 3 );\r
+                                               chars = false;\r
+                                       };\r
+       \r
+                               // end tag\r
+                               } else if ( html.indexOf("</") === 0 ) {\r
+                                       if ( 2 < ( index = _parseEndTag( stack, handler, html ) ) ) {\r
+                                               html = html.substring( index );\r
+                                               chars = false;\r
+                                       };\r
+       \r
+                               // start tag\r
+                               } else if ( html.indexOf("<") === 0 ) {\r
+                                       if( index = _parseStartTag( stack, last, handler, html ) ){\r
+                                               html  = html.substring( index );\r
+                                               chars = false;\r
+                                       } else\r
+                                       if( index === false ){\r
+                                               return;\r
+                                       };\r
+                               };\r
+\r
+                               if ( chars ) {\r
+                                       index = html.indexOf("<");\r
+                                       \r
+                                       text = index < 0 ? html : html.substring( 0, index );\r
+                                       html = index < 0 ? '' : html.substring( index );\r
+                                       \r
+                                       handler.chars( text );\r
+                               };\r
+\r
+                       };\r
+\r
+                       if( html === lastHtml ){\r
+                               handler.err( html );\r
+                               return;\r
+                       };\r
+                       \r
+                       if( async && startTime + 15 <= X.getTime() && html ){\r
+                               handler.progress( 1 - html.length / async[ 0 ] );\r
+                               X.Timer.once( 0, X.Dom.Parser.exec, [ html, handler, async ] );\r
+                               return;\r
+                       };\r
+                       \r
+                       lastHtml = html;\r
+               };\r
+               \r
+               // Clean up any remaining tags\r
+               X.Dom.Parser.parseEndTag( stack, handler );\r
+               \r
+               async && handler.complete();\r
+       },\r
+\r
+       _parseStartTag : function( stack, last, handler, html ){\r
+               var alphabets = X.Dom.Parser.alphabets,\r
+                       whiteSpace = X.Dom.Parser.whiteSpace,\r
+                       saveAttr = X.Dom.Parser.saveAttr,\r
+                       uri   = X.Dom.DTD.ATTR_VAL_IS_URI,\r
+                       phase = 0,\r
+                       l     = html.length,\r
+                       i     = 0,\r
+                       attrs = [],\r
+                       tagName, empty = false,\r
+                       chr, start, attrName, quot, escape;\r
+               \r
+               while( i < l && phase < 9 ){\r
+                       chr = html.charAt( i );\r
+                       switch( phase ){\r
+                               case 0 :\r
+                                       chr === '<' && ( ++phase );\r
+                                       break;\r
+                               case 1 : // タグ名の開始を待つ\r
+                                       alphabets.indexOf( chr ) !== -1 && ( ++phase && ( start = i ) );\r
+                                       break;\r
+                               case 2 : // タグ名の終わりの空白文字を待つ\r
+                                       whiteSpace.indexOf( chr ) !== -1 ?\r
+                                               ( ++phase && ( tagName = html.substring( start, i ) ) ) :\r
+                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
+                                               ( ( tagName = html.substring( start, i ) ) && ( phase = 9 ) );\r
+                                       break;\r
+                               case 3 : // 属性名の開始を待つ\r
+                                       alphabets.indexOf( chr ) !== -1 ?\r
+                                               ( ++phase && ( start = i ) ) :\r
+                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
+                                               ( phase = 9 );\r
+                                       break;\r
+                               case 4 : // 属性名の終わりを待つ\r
+                                       chr === '=' ?\r
+                                               ( ( phase = 6 ) && ( attrName = html.substring( start, i ) ) ) :\r
+                                       whiteSpace.indexOf( chr ) !== -1 &&\r
+                                               ( ( phase = 5 ) && ( attrName = html.substring( start, i ) ) );\r
+                                       break;\r
+                               case 5 : // 属性の = または次の属性または htmlタグの閉じ\r
+                                       whiteSpace.indexOf( chr ) !== -1 ?// ie4 未対応の属性には cite = http:// となる\r
+                                               1 :\r
+                                       alphabets.indexOf( chr ) !== -1 ?\r
+                                               ( ( phase = 4 ) && ( attrs[ attrs.length ] = attrName ) && ( start = i ) ) :\r
+                                       chr === '=' ?\r
+                                               ( phase = 6 ) :\r
+                                       ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
+                                               ( ( phase = 9 ) && ( attrs[ attrs.length ] = attrName ) );\r
+                                       break;\r
+                               case 6 : // 属性値の開始 quot を待つ\r
+                                       ( chr === '"' || chr === "'" ) ?\r
+                                               ( ( phase = 7 ) && ( quot = chr ) && ( start = i + 1 ) ):\r
+                                       whiteSpace.indexOf( chr ) === -1 &&\r
+                                               ( ( phase = 8 ) && ( start = i ) ); // no quot\r
+                                       break;\r
+                               case 7 : //属性値の閉じ quot を待つ\r
+                                       !escape && ( chr === quot ) && ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) );\r
+                                       break;\r
+                               case 8 : //閉じ quot のない属性の値\r
+                                       whiteSpace.indexOf( chr ) !== -1 ?\r
+                                               ( ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) ) :\r
+                                       ( chr === '>' ) ?\r
+                                               ( ( phase = 9 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) ) :\r
+                                       !escape && !uri[ attrName ] && ( empty = html.substr( i, 2 ) === '/>' ) && // attr の val が uri で / で終わりかつ、未対応属性の場合\r
+                                               ( phase = 9 );\r
+                                       break;\r
+                       };\r
+                       escape = chr === '\\' && !escape; // \\\\ is not escape for "\r
+                       ++i;\r
+               };\r
+               if( phase === 9 ){\r
+                       if( empty ) ++i;\r
+                       if( X.Dom.Parser.parseStartTag( stack, last, handler, tagName, attrs, empty, i ) === false ) return false;\r
+                       return i;\r
+               };\r
+               return 0; // error\r
+       },\r
+\r
+       _parseEndTag : function( stack, handler, html ){\r
+               var alphabets = X.Dom.Parser.alphabets,\r
+                       whiteSpace = X.Dom.Parser.whiteSpace,\r
+                       phase = 0,\r
+                       l     = html.length,\r
+                       i     = 0,\r
+                       tagName,\r
+                       chr, start;\r
+               \r
+               while( i < l && phase < 9 ){\r
+                       chr = html.charAt( i );\r
+                       switch( phase ){\r
+                               case 0 :\r
+                                       html.substr( i, 2 ) === '</' && ( ++phase && ++i );\r
+                                       break;\r
+                               case 1 : // タグ名の開始を待つ\r
+                                       alphabets.indexOf( chr ) !== -1 && ( ++phase && ( start = i ) );\r
+                                       break;\r
+                               case 2 : // タグ名の終わりの空白文字を待つ\r
+                                       whiteSpace.indexOf( chr ) !== -1 && ( ++phase );\r
+                                       ( chr === '>' ) && ( phase = 9 );\r
+                                       ( phase !== 2 ) && ( tagName = html.substring( start, i ) );\r
+                                       break;\r
+                               case 3 : // タグの終了を待つ\r
+                                       chr === '>' && ( phase = 9 );\r
+                                       break;\r
+                       };\r
+                       ++i;\r
+               };\r
+               if( phase === 9 ){\r
+                       X.Dom.Parser.parseEndTag( stack, handler, tagName );\r
+                       return i;\r
+               };\r
+               return 0; // error\r
+       },\r
+\r
+       saveAttr : function( attrs, name, value ){\r
+               name  = name.toLowerCase();\r
+               value = X.Dom.Parser.fillAttrs[ name ] === 1 ? name : value;\r
+               attrs[ attrs.length ] = {\r
+                       name    : name,\r
+                       value   : value,\r
+                       escaped :\r
+                               value.indexOf( '"' ) !== -1 ?\r
+                                       value.split( '"' ).join( '\\"' ).split( '\\\\"' ).join( '\\"' ) :\r
+                                       value\r
+               };\r
+       },\r
+\r
+       parseStartTag : function( stack, last, handler, tagName, attrs, unary, index ) {\r
+               var tagLower = tagName.toLowerCase(),\r
+                       inline   = X.Dom.Parser.inline,\r
+                       parseEndTag = X.Dom.Parser.parseEndTag,\r
+                       sisters  = X.Dom.Parser.sisters;\r
+               if ( X.Dom.Parser.block[ tagLower ] === 1 ) {\r
+                       while ( last && inline[ last.toLowerCase() ] === 1 ) {\r
+                               parseEndTag( stack, handler, last );\r
+                               last = stack[ stack.length - 1 ];\r
+                       };\r
+               };\r
+               X.Dom.Parser.closeSelf[ tagLower ] === 1 && ( last === tagName || ( sisters[ tagLower ] && sisters[ tagLower ][ last.toLowerCase() ] === 1 ) ) && parseEndTag( stack, handler, last );\r
+               unary = unary || X.Dom.Parser.empty[ tagName.toUpperCase() ];\r
+               !unary && ( stack[ stack.length ] = tagName );\r
+               \r
+               return handler.start( tagName, attrs, unary, index );\r
+       },\r
+\r
+       parseEndTag : function( stack, handler, tagName ) {\r
+               var pos = 0, i = stack.length;\r
+               // If no tag name is provided, clean shop\r
+               \r
+               // Find the closest opened tag of the same type\r
+               if ( tagName )\r
+                       for ( pos = i; 0 <= pos; )\r
+                               if ( stack[ --pos ] === tagName )\r
+                                       break;\r
+               \r
+               if ( 0 <= pos ) {\r
+                       // Close all the open elements, up the stack\r
+                       for ( ; pos < i; )\r
+                               handler.end( stack[ --i ] );\r
+                       \r
+                       // Remove the open elements from the stack\r
+                       stack.length = pos;\r
+               };\r
+       }\r
+       \r
+};\r
+\r
+X.Dom._htmlStringToXNode = {\r
+       flat : null,\r
+       nest : [],\r
+       err : function( html ){\r
+               X.Dom._htmlStringToXNode.flat.length = 0;\r
+               X.Dom._htmlStringToXNode.ignoreError !== true && X.Notification.warn( 'X.Dom.Parser() error ' + html );\r
+       },\r
+       start : function( tagName, attrs, noChild, length ){\r
+               var xnode,\r
+                       nest   = X.Dom._htmlStringToXNode.nest,\r
+                       flat   = X.Dom._htmlStringToXNode.flat,\r
+                       l      = nest.length,\r
+                       attr, name, i, _attrs; //, toIndex;\r
+               if( l ){\r
+                       xnode = nest[ l - 1 ].create( tagName );\r
+               } else {\r
+                       xnode = flat[ flat.length ] = X.Dom.Node.create( tagName );\r
+               };\r
+               if( !noChild ) nest[ l ] = xnode;\r
+               if( i = attrs.length ){\r
+                       _attrs = {};\r
+                       for( ; i; ){\r
+                               if( attr = attrs[ --i ] ){\r
+                                       if( typeof attr === 'string' ){\r
+                                               name = attr;\r
+                                               _attrs[ name ] = true;\r
+                                       } else {\r
+                                               name = attr.name;\r
+                                               _attrs[ name ] = attr.escaped;\r
+                                       };\r
+                               };\r
+                       };\r
+                       xnode.attr( _attrs );\r
+               };\r
+       },\r
+       end : function(){\r
+               0 < X.Dom._htmlStringToXNode.nest.length && ( --X.Dom._htmlStringToXNode.nest.length );\r
+       },\r
+       chars : function( text ){\r
+               if( X.Dom._htmlStringToXNode.nest.length ){\r
+                       X.Dom._htmlStringToXNode.nest[ X.Dom._htmlStringToXNode.nest.length - 1 ].createText( text );\r
+               } else {\r
+                       X.Dom._htmlStringToXNode.flat[ X.Dom._htmlStringToXNode.flat.length ] = X.Dom.Node.createText( text );\r
+               };\r
+       },\r
+       comment : X.emptyFunction\r
+};\r
+\r
+X.Dom.parse = function( html, ignoreError ){\r
+       var worker = X.Dom._htmlStringToXNode, ret;\r
+       worker.flat = [];\r
+       worker.nest.length = 0;\r
+       worker.ignoreError = ignoreError;\r
+       X.Dom.Parser.exec( html, worker );\r
+       ret = worker.flat;\r
+       delete worker.flat;\r
+       return ret;\r
+};\r
+\r
+X.Dom._asyncHtmlStringToXNode = {\r
+       err : function( html ){\r
+               X.Dom._htmlStringToXNode.err( html );\r
+               this.asyncDispatch( 0, { type : X.Event.ERROR } );\r
+       },\r
+       start   : X.Dom._htmlStringToXNode.start,\r
+       end     : X.Dom._htmlStringToXNode.end,\r
+       chars   : X.Dom._htmlStringToXNode.chars,\r
+       comment : X.emptyFunction,\r
+       \r
+       progress : function( pct ){\r
+               this.asyncDispatch( 0, { type : X.Event.PROGRESS, progress : pct } );\r
+       },\r
+       complete : function(){\r
+               var ret = X.Dom._htmlStringToXNode.flat;\r
+               delete X.Dom._htmlStringToXNode.flat;\r
+               this.asyncDispatch( 0, { type : X.Event.SUCCESS, xnodes : ret } );\r
+       }\r
+};\r
+\r
+X.Dom.asyncParse = function( html, ignoreError ){\r
+       var dispatcher = X.Class._override( new X.EventDispatcher(), X.Dom._asyncHtmlStringToXNode ),\r
+               worker = X.Dom._htmlStringToXNode;\r
+       dispatcher.listenOnce( X.Event.SUCCESS, dispatcher, dispatcher.kill );\r
+       worker.flat = [];\r
+       worker.nest.length = 0;\r
+       worker.ignoreError = ignoreError;\r
+       X.Dom.Parser.exec( html, dispatcher, [ html.length, [] ] );\r
+       return dispatcher;\r
+};\r