OSDN Git Service

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