OSDN Git Service

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