OSDN Git Service

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