OSDN Git Service

Version 0.6.133, fix for closure compiler - ADVANCED_OPTIMIZATIONS
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 09_XHTMLParser.js
1 \r
2 /*\r
3  * Original code by Erik John Resig (ejohn.org)\r
4  * http://ejohn.org/blog/pure-javascript-html-parser/\r
5  *\r
6  */\r
7 \r
8 var X_HTMLParser_CHARS = {\r
9                 A:1,B:1,C:1,D:1,E:1,F:1,G:1,H:1,I:1,J:1,K:1,L:1,M:1,N:1,O:1,P:1,Q:1,R:1,S:1,T:1,U:1,V:1,W:1,X:1,Y:1,Z:1,\r
10                 a:2,b:2,c:2,d:2,e:2,f:2,g:2,h:2,i:2,j:2,k:2,l:2,m:2,n:2,o:2,p:2,q:2,r:2,s:2,t:2,u:2,v:2,w:2,x:2,y:2,z:2,\r
11                 // "0" : 4, "1" : 4, "2" : 4, "3" : 4, "4" : 4, "5" : 4, "6" : 4, "7" : 4, "8" : 4, "9" : 4, closure compiler で minify すると ie4 で error、eval使う\r
12                 \r
13                 '\t' : 16, '\r\n' : 16, '\r' : 16, '\n' : 16, '\f' : 16, '\b' : 16, ' ' : 16\r
14         },\r
15         X_HTMLParser_alphabets  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',\r
16         X_HTMLParser_whiteSpace = '\t\r\n\f\b ',\r
17 \r
18         // Empty Elements - HTML 4.01\r
19         X_HTMLParser_empty = X_Dom_DTD_EMPTY,\r
20 \r
21         // Block Elements - HTML 4.01\r
22         X_HTMLParser_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,\r
23                 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
24         // Inline Elements - HTML 4.01\r
25         X_HTMLParser_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,\r
26                 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
27         // Elements that you can, intentionally, leave open\r
28         // (and which close themselves)\r
29         X_HTMLParser_closeSelf = {OLGROUP: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
30 \r
31         X_HTMLParser_sisters = {\r
32                 TH : { TD : 1 },\r
33                 TD : { TH : 1 },\r
34                 DT : { DD : 1 },\r
35                 DD : { DT : 1 },\r
36                 COLGROUP : { CAPTION : 1 },\r
37                 THEAD    : { CAPTION : 1, COLGROUP : 1 },\r
38                 TFOOT    : { CAPTION : 1, COLGROUP : 1, THEAD : 1, TBODY : 1 },\r
39                 TBODY    : { CAPTION : 1, COLGROUP : 1, THEAD : 1, TFOOT : 1 }\r
40         },\r
41         /*\r
42          * http://www.tohoho-web.com/html/tbody.htm\r
43          * HTML4.01では、ヘッダとフッタを先読みして表示するために、<tbody> よりも <tfoot> の方を先に記述しなくてはならないと定義されています。\r
44          * IE5.0 などでは HEAD → BODY → FOOT の順に表示するのですが、\r
45          * <tfoot> に未対応の古いブラウザでは、HEAD → FOOT → BODY の順に表示されてしまいます。\r
46          * また、HTML5 では、<tfoot> と <tbody> の順番はどちらでもよいことになりました。\r
47          */\r
48 \r
49         // Attributes that have their values filled in disabled="disabled"\r
50         X_HTMLParser_fillAttrs = X_Node_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
51 \r
52         // Special Elements (can contain anything)\r
53         X_HTMLParser_special = { SCRIPT : 1, STYLE : 1, PLAINTEXT : 1, XMP : 1, TEXTAREA : 1 },\r
54         \r
55         X_HTMLParser_skipFixNesting = false;\r
56         \r
57         function X_HTMLParser_exec( html, handler, async ){\r
58                 var special        = X_HTMLParser_special,\r
59                         //plainText      = X_HTMLParser_plainText,\r
60                         startTime      = async && X_Timer_now(),\r
61                         _parseStartTag = X_HTMLParser__parseStartTag,\r
62                         _parseEndTag   = X_HTMLParser__parseEndTag,\r
63                         stack          = async ? async[ 1 ] : [],\r
64                         lastHtml       = html,\r
65                         chars, last, text, index;\r
66 \r
67                 while ( html ) {\r
68                         chars = true;\r
69                         last  = stack[ stack.length - 1 ];\r
70                         \r
71                         // Make sure we're not in a script or style element\r
72                         if ( last && special[ last ] === 1 ) {\r
73                                 if( 0 <= ( index = html.toUpperCase().indexOf( '</' + last ) ) ){\r
74                                         handler.chars( html.substring( 0, index ) );\r
75                                         if( index = _parseEndTag( stack, handler, html ) ){\r
76                                                 html = html.substring( index );\r
77                                         } else {\r
78                                                 handler.chars( html );\r
79                                                 html = '';\r
80                                         };\r
81                                 } else {\r
82                                         handler.chars( html );\r
83                                         html = '';\r
84                                 };\r
85                         } else {\r
86                                 // Comment\r
87                                 if ( html.indexOf("<!--") === 0 ) {\r
88                                         if ( 0 < ( index = html.indexOf("-->") ) ) {\r
89                                                 handler.comment( html.substring( 4, index ) );\r
90                                                 html = html.substring( index + 3 );\r
91                                                 chars = false;\r
92                                         };\r
93         \r
94                                 // end tag\r
95                                 } else if ( html.indexOf("</") === 0 ) {\r
96                                         if ( 2 < ( index = _parseEndTag( stack, handler, html ) ) ) {\r
97                                                 html = html.substring( index );\r
98                                                 chars = false;\r
99                                         };\r
100         \r
101                                 // start tag\r
102                                 } else if ( html.indexOf("<") === 0 ) {\r
103                                         if( index = _parseStartTag( stack, last, handler, html ) ){\r
104                                                 html  = html.substring( index );\r
105                                                 chars = false;\r
106                                         } else\r
107                                         if( index === false ){\r
108                                                 return;\r
109                                         };\r
110                                 };\r
111 \r
112                                 if ( chars ) {\r
113                                         index = html.indexOf("<");\r
114                                         \r
115                                         text = index < 0 ? html : html.substring( 0, index );\r
116                                         html = index < 0 ? '' : html.substring( index );\r
117                                         \r
118                                         handler.chars( text );\r
119                                 };\r
120 \r
121                         };\r
122 \r
123                         if( html === lastHtml ){\r
124                                 handler.err( html );\r
125                                 return;\r
126                         };\r
127                         \r
128                         if( async && startTime + 15 <= X_Timer_now() && html ){\r
129                                 handler.progress( 1 - html.length / async[ 0 ] );\r
130                                 X.Timer.once( 0, X_HTMLParser_exec, [ html, handler, async ] );\r
131                                 return;\r
132                         };\r
133                         \r
134                         lastHtml = html;\r
135                 };\r
136                 \r
137                 // Clean up any remaining tags\r
138                 X_HTMLParser_parseEndTag( stack, handler );\r
139                 \r
140                 async && handler.complete();\r
141         };\r
142 \r
143         function X_HTMLParser__parseStartTag( stack, last, handler, html ){\r
144                 var alphabets = X_HTMLParser_CHARS,\r
145                         whiteSpace = X_HTMLParser_CHARS,\r
146                         saveAttr = X_HTMLParser_saveAttr,\r
147                         uri   = X_Dom_DTD_ATTR_VAL_IS_URI,\r
148                         phase = 0,\r
149                         l     = html.length,\r
150                         i     = 0,\r
151                         attrs = [],\r
152                         tagName, empty = false,\r
153                         chr, start, attrName, quot, escape;\r
154                 \r
155                 while( i < l && phase < 9 ){\r
156                         chr = html.charAt( i );\r
157                         switch( phase ){\r
158                                 case 0 :\r
159                                         chr === '<' && ( ++phase );\r
160                                         break;\r
161                                 case 1 : // タグ名の開始を待つ\r
162                                         ( alphabets[ chr ] & 3 ) && ( ++phase && ( start = i ) );\r
163                                         break;\r
164                                 case 2 : // タグ名の終わりの空白文字を待つ\r
165                                         ( whiteSpace[ chr ] & 16 ) ?\r
166                                                 ( ++phase && ( tagName = html.substring( start, i ) ) ) :\r
167                                         ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
168                                                 ( ( tagName = html.substring( start, i ) ) && ( phase = 9 ) );\r
169                                         break;\r
170                                 case 3 : // 属性名の開始を待つ\r
171                                         ( alphabets[ chr ] & 3 ) ?\r
172                                                 ( ++phase && ( start = i ) ) :\r
173                                         ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
174                                                 ( phase = 9 );\r
175                                         break;\r
176                                 case 4 : // 属性名の終わりを待つ\r
177                                         chr === '=' ?\r
178                                                 ( ( phase = 6 ) && ( attrName = html.substring( start, i ) ) ) :\r
179                                         ( whiteSpace[ chr ] & 16 ) &&\r
180                                                 ( ( phase = 5 ) && ( attrName = html.substring( start, i ) ) );\r
181                                         break;\r
182                                 case 5 : // 属性の = または次の属性または htmlタグの閉じ\r
183                                         ( whiteSpace[ chr ] & 16 ) ?// ie4 未対応の属性には cite = http:// となる\r
184                                                 1 :\r
185                                         ( alphabets[ chr ] & 3 ) ?\r
186                                                 ( ( phase = 4 ) && ( attrs[ attrs.length ] = attrName ) && ( start = i ) ) :\r
187                                         chr === '=' ?\r
188                                                 ( phase = 6 ) :\r
189                                         ( chr === '>' || ( empty = html.substr( i, 2 ) === '/>' ) ) &&\r
190                                                 ( ( phase = 9 ) && ( attrs[ attrs.length ] = attrName ) );\r
191                                         break;\r
192                                 case 6 : // 属性値の開始 quot を待つ\r
193                                         ( chr === '"' || chr === "'" ) ?\r
194                                                 ( ( phase = 7 ) && ( quot = chr ) && ( start = i + 1 ) ):\r
195                                         !( whiteSpace[ chr ] & 16 ) &&\r
196                                                 ( ( phase = 8 ) && ( start = i ) ); // no quot\r
197                                         break;\r
198                                 case 7 : //属性値の閉じ quot を待つ\r
199                                         !escape && ( chr === quot ) && ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) );\r
200                                         break;\r
201                                 case 8 : //閉じ quot のない属性の値\r
202                                         ( whiteSpace[ chr ] & 16 ) ?\r
203                                                 ( ( phase = 3 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) ) :\r
204                                         ( chr === '>' ) ?\r
205                                                 ( ( phase = 9 ) && saveAttr( attrs, attrName, html.substring( start, i ) ) ) :\r
206                                         !escape && !uri[ attrName ] && ( empty = html.substr( i, 2 ) === '/>' ) && // attr の val が uri で / で終わりかつ、未対応属性の場合\r
207                                                 ( phase = 9 );\r
208                                         break;\r
209                         };\r
210                         escape = chr === '\\' && !escape; // \\\\ is not escape for "\r
211                         ++i;\r
212                 };\r
213                 if( phase === 9 ){\r
214                         if( empty ) ++i;\r
215                         if( X_HTMLParser_parseStartTag( stack, last, handler, tagName.toUpperCase(), attrs, empty, i ) === false ) return false;\r
216                         return i;\r
217                 };\r
218                 return 0; // error\r
219         };\r
220 \r
221         function X_HTMLParser__parseEndTag( stack, handler, html ){\r
222                 var alphabets = X_HTMLParser_CHARS,\r
223                         whiteSpace = X_HTMLParser_CHARS,\r
224                         phase = 0,\r
225                         l     = html.length,\r
226                         i     = 0,\r
227                         tagName,\r
228                         chr, start;\r
229                 \r
230                 while( i < l && phase < 9 ){\r
231                         chr = html.charAt( i );\r
232                         switch( phase ){\r
233                                 case 0 :\r
234                                         html.substr( i, 2 ) === '</' && ( ++phase && ++i );\r
235                                         break;\r
236                                 case 1 : // タグ名の開始を待つ\r
237                                         ( alphabets[ chr ] & 3 ) && ( ++phase && ( start = i ) );\r
238                                         break;\r
239                                 case 2 : // タグ名の終わりの空白文字を待つ\r
240                                         ( whiteSpace[ chr ] & 16 ) && ( ++phase );\r
241                                         ( chr === '>' ) && ( phase = 9 );\r
242                                         ( phase !== 2 ) && ( tagName = html.substring( start, i ) );\r
243                                         break;\r
244                                 case 3 : // タグの終了を待つ\r
245                                         chr === '>' && ( phase = 9 );\r
246                                         break;\r
247                         };\r
248                         ++i;\r
249                 };\r
250                 if( phase === 9 ){\r
251                         X_HTMLParser_parseEndTag( stack, handler, tagName.toUpperCase() );\r
252                         return i;\r
253                 };\r
254                 return 0; // error\r
255         };\r
256 \r
257         function X_HTMLParser_saveAttr( attrs, name, value ){\r
258                 name  = name.toLowerCase();\r
259                 value = X_HTMLParser_fillAttrs[ name ] === 1 ? name : value;\r
260                 attrs[ attrs.length ] = {\r
261                         name    : name,\r
262                         value   : value,\r
263                         escaped :\r
264                                 value.indexOf( '"' ) !== -1 ?\r
265                                         value.split( '"' ).join( '\\"' ).split( '\\\\"' ).join( '\\"' ) :\r
266                                         value\r
267                 };\r
268         };\r
269 \r
270         function X_HTMLParser_parseStartTag( stack, last, handler, tagName, attrs, empty, index ) {\r
271                 var inline   = X_HTMLParser_inline,\r
272                         parseEndTag = X_HTMLParser_parseEndTag,\r
273                         sisters  = X_HTMLParser_sisters;\r
274                 if( !X_HTMLParser_skipFixNesting && X_HTMLParser_block[ tagName ] === 1 ){\r
275                         while( last && inline[ last ] === 1 ){\r
276                                 parseEndTag( stack, handler, last );\r
277                                 last = stack[ stack.length - 1 ];\r
278                         };\r
279                 };\r
280                 last && X_HTMLParser_closeSelf[ tagName ] === 1 && ( last === tagName || ( sisters[ tagName ] && sisters[ tagName ][ last ] === 1 ) ) && parseEndTag( stack, handler, last );\r
281                 empty = empty || X_HTMLParser_empty[ tagName ];\r
282                 !empty && ( stack[ stack.length ] = tagName );\r
283                 \r
284                 return handler.start( tagName, attrs, empty, index );\r
285         };\r
286 \r
287         function X_HTMLParser_parseEndTag( stack, handler, tagName ) {\r
288                 var pos = 0, i = stack.length;\r
289                 // If no tag name is provided, clean shop\r
290                 \r
291                 // Find the closest opened tag of the same type\r
292                 if ( tagName )\r
293                         for ( pos = i; 0 <= pos; )\r
294                                 if ( stack[ --pos ] === tagName )\r
295                                         break;\r
296                 \r
297                 if ( 0 <= pos ) {\r
298                         // Close all the open elements, up the stack\r
299                         for ( ; pos < i; )\r
300                                 handler.end( stack[ --i ] );\r
301                         \r
302                         // Remove the open elements from the stack\r
303                         stack.length = pos;\r
304                 };\r
305         };\r
306 \r
307 var X_HTMLParser_htmlStringToXNode = {\r
308         flat : null,\r
309         nest : [],\r
310         err : function( html ){\r
311                 X_HTMLParser_htmlStringToXNode.flat.length = 0;\r
312                 !X_HTMLParser_htmlStringToXNode.ignoreError && X.Logger.warn( 'X_Dom_Parser() error ' + html );\r
313         },\r
314         start : function( tagName, attrs, noChild, length ){\r
315                 var xnode,\r
316                         nest   = X_HTMLParser_htmlStringToXNode.nest,\r
317                         flat   = X_HTMLParser_htmlStringToXNode.flat,\r
318                         l      = nest.length,\r
319                         attr, name, i, _attrs; //, toIndex;\r
320                 if( l ){\r
321                         xnode = nest[ l - 1 ].create( tagName );\r
322                 } else {\r
323                         xnode = flat[ flat.length ] = X_Doc_create( tagName );\r
324                 };\r
325                 if( !noChild ) nest[ l ] = xnode;\r
326                 if( i = attrs.length ){\r
327                         _attrs = {};\r
328                         for( ; i; ){\r
329                                 if( attr = attrs[ --i ] ){\r
330                                         if( X_Type_isString( attr ) ){\r
331                                                 name = attr;\r
332                                                 _attrs[ name ] = true;\r
333                                         } else {\r
334                                                 name = attr.name;\r
335                                                 _attrs[ name ] = attr.escaped;\r
336                                         };\r
337                                 };\r
338                         };\r
339                         xnode.attr( _attrs );\r
340                 };\r
341         },\r
342         end : function(){\r
343                 0 < X_HTMLParser_htmlStringToXNode.nest.length && ( --X_HTMLParser_htmlStringToXNode.nest.length );\r
344         },\r
345         chars : function( text ){\r
346                 if( X_HTMLParser_htmlStringToXNode.nest.length ){\r
347                         X_HTMLParser_htmlStringToXNode.nest[ X_HTMLParser_htmlStringToXNode.nest.length - 1 ].createText( text );\r
348                 } else {\r
349                         X_HTMLParser_htmlStringToXNode.flat[ X_HTMLParser_htmlStringToXNode.flat.length ] = X_Doc_createText( text );\r
350                 };\r
351         },\r
352         comment : X.emptyFunction\r
353 };\r
354 \r
355 function X_HtmlParser_parse( html, ignoreError ){\r
356         var worker = X_HTMLParser_htmlStringToXNode, ret;\r
357         worker.flat = [];\r
358         worker.nest.length = 0;\r
359         worker.ignoreError = ignoreError;\r
360         X_HTMLParser_exec( html, worker );\r
361         ret = worker.flat;\r
362         delete worker.flat;\r
363         return ret;\r
364 };\r
365 \r
366 var X_HTMLParser_asyncHtmlStringToXNode = {\r
367         err : function( html ){\r
368                 X_HTMLParser_htmlStringToXNode.err( html );\r
369                 this.asyncDispatch( X_Event.ERROR );\r
370         },\r
371         start   : X_HTMLParser_htmlStringToXNode.start,\r
372         end     : X_HTMLParser_htmlStringToXNode.end,\r
373         chars   : X_HTMLParser_htmlStringToXNode.chars,\r
374         comment : X.emptyFunction,\r
375         \r
376         progress : function( pct ){\r
377                 this.asyncDispatch( { type : X_Event.PROGRESS, percent : pct } );\r
378         },\r
379         complete : function(){\r
380                 var ret = X_HTMLParser_htmlStringToXNode.flat;\r
381                 delete X_HTMLParser_htmlStringToXNode.flat;\r
382                 this.asyncDispatch( { type : X_Event.SUCCESS, xnodes : ret } );\r
383         }\r
384 };\r
385 \r
386 function X_HTMLParser_asyncParse( html, ignoreError ){\r
387         var dispatcher = X_Class_override( new X.EventDispatcher(), X_HTMLParser_asyncHtmlStringToXNode ),\r
388                 worker = X_HTMLParser_htmlStringToXNode;\r
389         dispatcher.listenOnce( X_Event.SUCCESS, dispatcher, dispatcher.kill );\r
390         worker.flat = [];\r
391         worker.nest.length = 0;\r
392         worker.ignoreError = ignoreError;\r
393         X_HTMLParser_exec( html, dispatcher, [ html.length, [] ] );\r
394         return dispatcher;\r
395 };\r