OSDN Git Service

0fd78c94a11b886956e7798b49251528f4c5a51e
[pettanr/clientJs.git] / 0.6.x / js / 02_dom / 08_XNodeSelector.js
1 /**\r
2  * Original code by ofk ( kQuery, ksk )\r
3  * http://d.hatena.ne.jp/ofk/comment/20090106/1231258010\r
4  * http://d.hatena.ne.jp/ofk/20090111/1231668170 \r
5  *\r
6  * セレクタ Level 3\r
7  * http://standards.mitsue.co.jp/resources/w3c/TR/css3-selectors/#nth-child-pseudo\r
8  */\r
9 \r
10 var\r
11         X_Node_Selector__PSEUDO    = {\r
12                 'nth-child'        : 9,\r
13                 'nth-last-child'   : 14,\r
14                 'nth-of-type'      : 11,\r
15                 'nth-last-of-type' : 16,\r
16                 root               : 4,\r
17                 link               : 4,\r
18                 lang               : 4,\r
19                 empty              : 5,\r
20                 target             : 6,\r
21                 invalid            : 7,\r
22                 enabled            : 7,\r
23                 checked            : 7,\r
24                 disabled           : 8,\r
25                 contains           : 8,\r
26                 'last-child'       : 10,\r
27                 'only-child'       : 10,                        \r
28                 'first-child'      : 11,\r
29                 'last-of-type'     : 12,\r
30                 'only-of-type'     : 12,                \r
31                 'first-of-type'    : 13\r
32         },\r
33         \r
34         X_Node_Selector__COMBINATOR = {\r
35                 ''    : 0, // none\r
36                 ' '   : 1, // 子孫セレクタ\r
37                 '>'   : 2, // 子セレクタ\r
38                 '+'   : 3, // 兄弟セレクタ,共通の親を持つ、1番目要素が2番目要素の1つ前にある\r
39                 '~'   : 4, // 一般兄弟セレクタ,共通の親を持つ、1番目要素が2番目要素の前 (直前でなくともよい) にある\r
40                 ','   : 5,\r
41                 '@'   : 6  // XML 用の拡張、属性ノードを辿る http://www.marguerite.jp/Nihongo/WWW/RefDOM/_Attr_interface.html\r
42         },\r
43         X_Node_Selector__SELECTOR = {\r
44                 ''    : 0, // none\r
45                 tag   : 1,\r
46                 '#'   : 2,\r
47                 '.'   : 3,\r
48                 ':'   : 4,\r
49                 '['   : 5,\r
50                 not   : 6,\r
51                 scope : 7,\r
52                 root  : 8,\r
53                 link  : 9\r
54         },\r
55         X_Node_Selector__OPERATORS = { '==' : 1, '!=': 2, '~=': 3, '^=': 4, '$=': 5, '*=': 6, '|=': 7 }, // '':0 は属性が存在するならtrue\r
56         // TODO { a : 1, A : 2, _ : 3,,, }\r
57         X_Node_Selector__ALPHABET  = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789\\',\r
58         X_Node_Selector__NUMBER    = '+-0123456789';\r
59 \r
60 // XMLWrapper のために今だけ外部に公開\r
61 X_NodeSelector_parse = X_Node_Selector__parse;\r
62                 \r
63 /*\r
64  * セレクタ文字列の解析、但し一挙に行わず、ひと塊づつ\r
65  * 結合子 + 単体セレクタ( タグ,*,#,.,[],: )\r
66  * ' ' 子孫セレクタ, '>' 直下,'+','~' の場合、tagName|* を返す\r
67  * return [ 今回のパースで解析した終端位置, [ selector, ... ] ] error: return pointer\r
68  */\r
69 function X_Node_Selector__parse( query, last ){\r
70         var COMBINATOR = X_Node_Selector__COMBINATOR,\r
71                 SELECTOR   = X_Node_Selector__SELECTOR,\r
72                 OPERATORS  = X_Node_Selector__OPERATORS,\r
73                 ALPHABET   = X_Node_Selector__ALPHABET,\r
74                 NUMBER     = X_Node_Selector__NUMBER,\r
75                 result     = [],\r
76                 i          = -1,\r
77                 l          = query.length,\r
78                 phase      = 0x0,\r
79                 combinator = 0,\r
80                 selector   = 0,\r
81                 chr, chrCode, nameChr, name1st,\r
82                 tmp, escape, quot, start,\r
83                 name, key, value, operator, a, b, not;\r
84         query += ' ';\r
85         while( i < l ){\r
86                 chr     = query.charAt( ++i );\r
87                 chrCode = ALPHABET.indexOf( chr );\r
88                 nameChr = chrCode !== -1;\r
89                 name1st = nameChr && chrCode < 52;\r
90                 switch( phase ){\r
91                         case 0x0 :\r
92                                 name1st ? // tagName\r
93                                         ( ( selector = 1 ) && ( phase = 0x2 ) && ( start = i ) ) :\r
94                                 !not && ( tmp = COMBINATOR[ chr ] ) ? (\r
95                                         ( 1 < tmp && 1 < combinator ) ?\r
96                                                 ( phase = 0xf ) :\r
97                                                 ( phase = tmp === 5 ? 0xe : 0x0 ) & ( ( 1 < tmp || combinator < 1 ) && ( combinator = tmp ) ) & ( tmp === 5 && ++i ) ) : // ' ' でない結合子の上書きはエラー\r
98                                 ( tmp = SELECTOR[ chr ] ) ? // [\r
99                                         ( selector = tmp ) && ( phase = selector === 5 ? 0x4 : 0x1 ) : // 7:[, 0<:\r
100                                 chr === '*' ?\r
101                                         ( ( selector = 1 ) && ( name = chr ) && ( phase = 0xe ) && ++i ) :\r
102                                         chr !== ' ' && ( phase = 0xf );\r
103                                 //console.log( '0x0: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase + ' tmp:' + tmp + ' comb:' + combinator );\r
104                                 break;\r
105                         case 0x1 :\r
106                                 name1st ?\r
107                                         ( ( start = i ) && ( phase = 0x2 ) ) :\r
108                                         chr !== ' ' && ( phase = 0xf );\r
109                                 break;\r
110                         case 0x2 :\r
111                                 !nameChr && !( escape && ( selector === 2 || selector === 3 ) && ( chr === ':' || chr === '.' ) ) ? // id or class の場合 : . を直前にエスケープした場合に限り使える\r
112                                         ( name = query.substring( start, i ) ) && ( phase = selector === 4 && name !== 'not' && chr === '(' ? ( name !== 'lang' && name !== 'contains' ? 0x8 : 0xb ) : 0xe ) :\r
113                                 SELECTOR[ chr ] < 4 && ( phase = 0xe );\r
114                                 break;\r
115                         \r
116                         case 0x3 : //:nth-\r
117                                 break;\r
118                         \r
119                         case 0x4 : // start attr filter\r
120                                 name1st ?\r
121                                         ( ( phase = 0x5 ) && ( start = i ) ) :\r
122                                         ( phase = 0xf );\r
123                                 break;\r
124                         case 0x5 : // attr filter key\r
125                                 chr === '=' ?\r
126                                         ( ( operator = 1 ) && ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = i + 1 ) ) :\r
127                                 chr === ']' ?\r
128                                         ( !( operator = 0 ) && ( phase = 0xe ) && ( key || ( key = query.substring( start, i ) ) ) && ++i ) :\r
129                                 chr === ' ' ?\r
130                                         ( key || ( key = query.substring( start, i ) ) ) :\r
131                                 ( operator = OPERATORS[ query.substr( i, 2 ) ] ) ?\r
132                                         ( ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = ++i ) ) :\r
133                                         !nameChr && ( phase = 0xf );\r
134                                 //console.log( name1st + ' ' + chrCode + chr + phase );\r
135                                 break;\r
136                         case 0x6 :\r
137                                 ( chr === '"' || chr === "'" ) && !escape && !quot ?\r
138                                         ( quot = chr ) && ( start = i + 1 ) && ( phase = 0x7 ) :\r
139                                         chr !== ' ' && ( start = i ) && ( phase = 0x7 );\r
140                                 break;\r
141                                 \r
142                         case 0x7 : // attr filter value\r
143                                 chr === quot ?\r
144                                         !escape && !value && ( value = query.substring( start, i ) ) :\r
145                                 chr === ']' ?\r
146                                         ( ( value || ( value = query.substring( start, i ) ) ) && ( phase = 0xe ) && ++i ) :    \r
147                                 chr === ' ' && !quot && !value && ( value = query.substring( start, i ) );                      \r
148                                         //( chr === '"' || chr === "'" ) && !quot && ( quot = chr ) && ( start = i + 1 );\r
149                                 break;\r
150                                 \r
151                         case 0x8 : // 4, 2n, even, odd, -n+4,\r
152                                 NUMBER.indexOf( chr ) !== -1 ?\r
153                                         ( start = i ) && ( phase = 0x9 ) :\r
154                                 chr === 'n' ?\r
155                                         ( phase = 0xa ) && ( a = 1 ) && ( start = i + 1 ) :\r
156                                 query.substr( i, 4 ) === 'even' ?\r
157                                         ( ( a = 2 ) && !( b = 0 ) && ( i += 3 ) ) :\r
158                                 query.substr( i, 3 ) === 'odd' ?\r
159                                         ( ( a = 2 ) && ( b = 1 ) && ( i += 2 ) ) :                                      \r
160                                 chr === ')' && ( phase = a ? 0xe : 0xf ) && ++i;\r
161                                 //console.log( '0x8: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase + ' ' + name );\r
162                                 break;\r
163                         case 0x9 :\r
164                                 tmp = query.substring( start, i );\r
165                                 chr === 'n' ?\r
166                                         ( phase = 0xa ) && ( start = i + 1 ) && ( a = tmp === '+' ? 1 : tmp === '-' ? -1 : parseFloat( tmp ) ) :\r
167                                 chr === ')' && ( phase = 0xe ) && ++i && ( b = parseFloat( tmp ) ) && ( a = 0 );\r
168                                 //console.log( '0x9: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase );\r
169                                 break;\r
170                         case 0xa :\r
171                                 chr === ')' && ( phase = 0xe ) && ++i && ( b = parseFloat( query.substring( start, i ) ) || 0 );\r
172                                 console.log( '0xa: ' + start + ' ' + i );\r
173                                 break;\r
174                         // contains, lang\r
175                         case 0xb :\r
176                                 ( chr === '"' || chr === "'" ) && !escape && !quot &&\r
177                                         ( quot = chr ) && ( start = i + 1 ) && ( phase = 0xc );\r
178                                 break;  \r
179                         case 0xc :\r
180                                 chr === quot && !escape && ( value = query.substring( start, i ) ) && ( phase = 0xd );\r
181                                 break;\r
182                         case 0xd :\r
183                                 chr === ')' && ( phase = 0xe ) && ++i;                          \r
184                                 break;\r
185                         default :\r
186                 };\r
187                 \r
188                 if( phase === 0xf ) return i;\r
189                 \r
190                 //alert( chr + ' ' + phase + ' ' + selector + ' ' + name + ' ' + name1st )\r
191                 if( phase === 0xe ){\r
192                         if( selector === 4 ){// :not\r
193                                 if( name === 'not' ){\r
194                                         if( not ){\r
195                                                 return i; // error\r
196                                         } else {\r
197                                                 not      = true;\r
198                                                 selector = 0;\r
199                                                 phase    = 0x0;\r
200                                                 name     = null;                                        \r
201                                         };\r
202                                         //continue;\r
203                                 } else\r
204                                 if( name === 'lang' || name === 'contains' ){\r
205                                         result = [ not ? 0 : combinator, selector, name, value ];\r
206                                         break;\r
207                                 } else {\r
208                                         if( a !== a || b !== b ) return i;\r
209                                         result = [ not ? 0 : combinator, selector, name, a, b ];\r
210                                         break;  \r
211                                 };\r
212                         } else {\r
213                                 result =\r
214                                         combinator === 5 ?\r
215                                                 5 :\r
216                                         selector === 5 ?\r
217                                                 [ not ? 0 : combinator, selector, key, operator, value ] :\r
218                                                 [ not ? 0 : combinator, selector, name.split( '\\' ).join( '' ) ];\r
219                                 break;                          \r
220                         };\r
221                 };\r
222 \r
223                 escape = chr === '\\' && !escape;\r
224         };\r
225         //if( phase !== 0xe ) return i;\r
226         if( not && ( tmp = query.substr( i ).indexOf( ')' ) ) === -1 ) return i;\r
227         return not ? [ i + tmp + 1, [ combinator, 6, result ] ] : [ i, result ];\r
228 };\r
229 \r
230         // セレクター\r
231         X.Doc.find = X_shortcutFunction = Node.prototype.find = X_NodeList.prototype.find = function ( queryString ){\r
232                 var HTML      = X_Node_html,\r
233                         scope     = this.constructor === X_NodeList && this.length ? this : [ this.constructor === Node ? this : X_Node_body ],\r
234                         parents   = scope, // 探索元の親要素 XNodeList の場合あり\r
235                         // TODO { title : true,,, }\r
236                         noLower   = 'title id name class for action archive background cite classid codebase data href longdesc profile src usemap',// + X_Dom_DTD_ATTR_VAL_IS_URI.join( ' ' ),\r
237                         ARY_PUSH  = Array.prototype.push,\r
238                         ret       = [], // 結果要素\r
239                         root      = X_Node_getRoot( scope[ 0 ] ),\r
240                         isXML     = !!X_Node_isXmlDocument( root ),\r
241                         isMulti   = 1 < scope.length,// 要素をマージする必要がある\r
242                         isStart   = true,\r
243                         _         = ' ',\r
244                         isAll, isNot, hasRoot,\r
245                         l, i, n, parsed,\r
246                         xnodes, // 一時保存用\r
247                         merge, // 要素がコメントノードで汚染されている場合使う\r
248                         combinator, selector, name, tagName,\r
249                         uid, tmp, xnode, filter, key, op, val, toLower, useName,\r
250             links, className, attr, flag;\r
251 \r
252                 /*@+debug[*/\r
253                 if( X_ViewPort_readyState < X_Event.XDOM_READY ){\r
254                         alert( 'not ready! use X.ViewPort.listenOnce( X_Event.XDOM_READY, callback )' );\r
255                         return;\r
256                 };\r
257                 /*]@+debug*/\r
258 \r
259                 // 文字列以外は空で返す\r
260                 if( typeof queryString !== 'string' ) return ret;\r
261                 \r
262                 xnodes = [];\r
263                 \r
264                 // 以下、パースと探索\r
265                 for( ; queryString.length; ){\r
266                         //console.log( 'queryString[' + queryString + ']' );\r
267                         \r
268                         // 初期化処理\r
269                         if( !parsed ){\r
270                                 parsed = X_Node_Selector__parse( queryString );\r
271                                 \r
272                                 if( typeof parsed === 'number' ){\r
273                                         // error\r
274                                         return [];\r
275                                 };\r
276                                 \r
277                                 queryString = queryString.substr( parsed[ 0 ] );\r
278                                 parsed      = parsed[ 1 ];\r
279                                 \r
280                                 //console.log( 'X_Node_Selector__parse ' + parsed );\r
281                                 \r
282                                 if( parsed === 5 ){\r
283                                         isMulti = true;\r
284                                         parents = scope;\r
285                                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
286                                         parsed  = null;\r
287                                         xnodes  = [];\r
288                                         isStart = true;\r
289                                         continue;\r
290                                 };\r
291                         };\r
292                         \r
293                         combinator  = parsed[ 0 ];\r
294                         selector    = parsed[ 1 ];\r
295                         name        = parsed[ 2 ];\r
296                         tagName     = selector === 1 ? ( isXML ? name : name.toUpperCase() ) : '*';\r
297                         isAll       = tagName === '*';\r
298         \r
299                         if( !isStart ){\r
300                                 if( !xnodes.length ){\r
301                                         parsed = null;\r
302                                         continue;                                       \r
303                                 } else\r
304                                 if( combinator !== 0 ){\r
305                                         parents = xnodes;\r
306                                         xnodes  = [];\r
307                                         //console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xnodes.length );\r
308                                 };\r
309                         };\r
310                         \r
311                         i = 0;\r
312                         l = parents.length;\r
313                         n = -1; \r
314                         isMulti = isMulti || 1 < l;\r
315                         \r
316                         //console.log( 'combinator ' + combinator );\r
317         \r
318                         switch( combinator ){\r
319                                 // > TagName|*\r
320                                 case 2 :\r
321                                         for( ; i < l; ++i ){\r
322                                                 for( xnode = parents[ i ].firstChild(); xnode; xnode = xnode.next() ){\r
323                                                         if( xnode._tag && ( isAll || tagName === xnode._tag ) ) xnodes[ ++n ] = xnode;\r
324                                                 };                              \r
325                                         };\r
326                                         break;\r
327                                 // + TagName|*\r
328                                 case 3 :\r
329                                         for( ; i < l; ++i ){\r
330                                                 for( xnode = parents[ i ].next(); xnode; xnode = xnode.next() ){\r
331                                                         if( xnode._tag ){\r
332                                                                 if( isAll || tagName === xnode._tag ) xnodes[ ++n ] = xnode;\r
333                                                                 break;\r
334                                                         };                                                                      \r
335                                                 };                                                              \r
336                                         };\r
337                                         break;\r
338                                 // ~ TagName|*\r
339                                 case 4 :\r
340                                         merge  = {};\r
341                                         for( ; i < l; ++i ){\r
342                                                 for( xnode = parents[ i ].next(); xnode; xnode = xnode.next() ){\r
343                                                         if( xnode._tag && ( isAll || tagName === xnode._tag ) ){\r
344                                                                 uid = xnode._uid;\r
345                                                                 if( merge[ uid ] ){\r
346                                                                         break;\r
347                                                                 } else {\r
348                                                                         merge[ uid ] = true;\r
349                                                                         xnodes[ ++n ] = xnode;\r
350                                                                 };\r
351                                                         };                                                                      \r
352                                                 };                                                              \r
353                                         };\r
354                                         break;\r
355                                 \r
356                                 // case 6 : 属性ノードは実装しない\r
357                                 \r
358                                 default :\r
359                                         if( combinator === 1 || ( isStart && selector < 7 ) ){\r
360                                                 //console.log( l + ' > ' + xnodes.length + ' tag:' + tagName );\r
361                                                 for( ; i < l; ++i ){\r
362                                                         xnode = parents[ i ];\r
363                                                         xnode._xnodes && xnode._xnodes.length && X_Node_Selector__fetchElements( xnodes, xnode, isAll ? null : tagName );\r
364                                                 };\r
365                                                 //console.log( l + ' >> ' + xnodes.length + ' tag:' + tagName );\r
366                                         };\r
367                         };\r
368                         \r
369                         isStart = false;\r
370                         \r
371                         //alert( 'pre-selector:' + ( xnodes && xnodes.length ) )\r
372                         \r
373                         switch( selector ){\r
374                                 // #, ID\r
375                                 case 2 :\r
376                                         filter = [ 'id', 1, name ]; break;\r
377                                 // ., class\r
378                                 case 3 :\r
379                                         filter = [ 'class', 3 /*'~='*/, name ]; break;\r
380                                 // :, 擬似クラス\r
381                                 case 4 :\r
382                                         if( !( filter = X_Node_Selector__filter[ name ] ) ){\r
383                                                 return [];\r
384                                         };\r
385                                         break;\r
386                                 // [] 属性\r
387                                 case 5 :\r
388                                         filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
389                                 // :not\r
390                                 case 6 :\r
391                                         isNot  = true;\r
392                                         parsed = parsed[ 2 ];\r
393                                         continue;\r
394                                 // scope\r
395                                 case 7 :\r
396                                         xnodes = scope; break;\r
397                                 // root\r
398                                 case 8 :\r
399                                         hasRoot = true;\r
400                                         xnodes = [ HTML ]; break;\r
401                                 // link\r
402                                 case 9 :\r
403                                         if( links = document.links ){\r
404                                                 for( xnodes = [], i = links.length; i; ){\r
405                                                         xnodes[ --i ] = new Node( links[ i ] );\r
406                                                 };\r
407                                         } else {\r
408                                                 // area[href],a[href]\r
409                                         };\r
410                         };\r
411                         \r
412                         if( filter && xnodes.length ){\r
413                                 // filter.mが関数の場合\r
414                                 if( filter.m ){\r
415                                         xnodes = filter.m(\r
416                                                 {\r
417                                                         not : isNot,\r
418                                                         xml : isXML\r
419                                                 },\r
420                                                 xnodes,\r
421                                                 parsed[ 3 ], parsed[ 4 ]\r
422                                         );\r
423                                 } else\r
424                                 // filterが関数の場合\r
425                                 if( typeof filter === 'function' ){\r
426                                         tmp = [];\r
427                                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
428                                                 if( ( !!filter( xnode ) ) ^ isNot ) tmp[ ++n ] = xnode; \r
429                                         };\r
430                                         xnodes = tmp;\r
431                                 } else {\r
432                                 // 属性セレクター                        \r
433                                         tmp = [];\r
434                                         key = filter[ 0 ];\r
435                                         op  = filter[ 1 ];\r
436                                         val = filter[ 2 ];\r
437                                         \r
438                                         key = X_Node_Attr_renameForTag[ key ] || key;\r
439                                         \r
440                                         // [class~='val']\r
441                                         if( !isXML && key === 'class' && op === 3 ){\r
442                                                 val = _ + val + _;\r
443                                                 for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
444                                                         className = xnode._className;\r
445                                                         if( !!( className && ( _ + className + _ ).indexOf( val ) > -1 ) ^ isNot ) tmp[ ++n ] = xnode;\r
446                                                 };\r
447                                         } else {\r
448                                         // 通常\r
449                                                 // 諦めて、funcAttrを呼ぶ\r
450                                                 // flag_call  = ($.browser.safari && key === 'selected');\r
451                                                 // getAttributeを使わない\r
452                                                 useName = X_UA[ 'IE' ] && key !== 'href' && key !== 'src';\r
453                                                 toLower = !!val && !isXML && noLower.indexOf( key ) === -1; //!noLower.test(key);\r
454                                                 if( toLower ) val = val.toLowerCase();\r
455                                                 if( op === 3 ) val = _ + val + _;\r
456 \r
457                                                 for( i = 0, n = -1, l = xnodes.length; i < l; ++i ){\r
458                                                         xnode = xnodes[ i ];\r
459                                                         attr =\r
460                                                                 key === 'id' ? xnode._id :\r
461                                                                 key === 'class' ? xnode._className :\r
462                                                                 xnode._attrs && xnode._attrs[ key ];\r
463                                                                 //flag_call ?\r
464                                                                 //      funcAttr( elem, key ) :\r
465                                                                 //useName ?\r
466                                                                 //      elem[ X_Node_Attr_renameForDOM[ key ] || key ] :\r
467                                                                 //      elem.getAttribute( key, 2 );\r
468                                                         flag = attr != null;// && ( !useName || attr !== '' );\r
469                                                         if( flag && op ){\r
470                                                                 if( toLower ) attr = attr.toLowerCase();\r
471                                                                 \r
472                                                                 switch( op ){\r
473                                                                         case 1: // =\r
474                                                                                 flag = attr === val;\r
475                                                                                 break;\r
476                                                                         case 2: // !=\r
477                                                                                 flag = attr !== val;\r
478                                                                                 break;\r
479                                                                         case 3: // ~=\r
480                                                                                 flag = ( _ + attr + _ ).indexOf( val ) !== -1;\r
481                                                                                 break;\r
482                                                                         case 4: // ^=\r
483                                                                                 flag = attr.indexOf( val ) === 0;\r
484                                                                                 break;\r
485                                                                         case 5: // $=\r
486                                                                                 flag = attr.lastIndexOf( val ) + val.length === attr.length;\r
487                                                                                 break;\r
488                                                                         case 6: // *=\r
489                                                                                 flag = attr.indexOf( val ) !== -1;\r
490                                                                                 break;\r
491                                                                         case 7: // |=\r
492                                                                                 flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';\r
493                                                                                 break;\r
494                                                                 };\r
495                                                         };\r
496                                                         if( !!flag ^ isNot ) tmp[ ++n ] = xnode;\r
497                                                 };\r
498                                         };\r
499                                         xnodes = tmp;\r
500                                 };\r
501                         };\r
502                         filter  = null;\r
503                         isNot   = false;\r
504                         parsed  = null;\r
505                         \r
506                         //console.log( '//end :' + ( xnodes && xnodes.length ) );\r
507                 };\r
508                 //console.log( 'multi:' + ( xnodes && xnodes.length ) );\r
509                 \r
510                 // tree 順に並び替え、同一要素の排除\r
511                 if( isMulti ){\r
512                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
513                         l = ret.length;\r
514                         if( l < 2 ) return ret[ 0 ] || X_Node_none;\r
515                         \r
516                         xnodes = [];\r
517                         merge  = {};\r
518                         for( i = 0, n = -1; i < l; ++i ){\r
519                                 //alert( 'multi:' + i )\r
520                                 xnode = ret[ i ];\r
521                                 if( !merge[ uid = xnode._uid ] ){\r
522                                         merge[ uid ] = true;\r
523                                         xnodes[ ++n ] = xnode;\r
524                                 };\r
525                         };\r
526                         X_Node_Selector__sortElementOrder( ret = [], xnodes, hasRoot ? [ HTML ] : HTML._xnodes );\r
527                         xnodes = ret;\r
528                 };\r
529 \r
530                 return xnodes.length === 1 ? xnodes[ 0 ] : new X_NodeList( xnodes );\r
531         };\r
532         \r
533         function X_Node_Selector__sortElementOrder( newList, list, xnodes ){\r
534                 var l = xnodes.length,\r
535                         i = 0,\r
536                         j, child, _xnodes;\r
537                 for( ; i < l; ++i ){\r
538                         child = xnodes[ i ];\r
539                         if( !child._tag ) continue;\r
540                         //console.log( child._tag );\r
541                         if( ( j = list.indexOf( child ) ) !== -1 ){\r
542                                 newList[ newList.length ] = child;\r
543                                 list.splice( j, 1 );\r
544                                 if( list.length === 1 ){\r
545                                         newList[ newList.length ] = list[ 0 ];\r
546                                         list.length = 0;\r
547                                         return true;\r
548                                 };\r
549                                 if( list.length === 0 ) return true;\r
550                         };\r
551                         if( ( _xnodes = child._xnodes ) && X_Node_Selector__sortElementOrder( newList, list, _xnodes ) ){\r
552                                 return true;\r
553                         };\r
554                 };\r
555         };\r
556         \r
557         function X_Node_Selector__fetchElements( list, parent, tag ){\r
558                 var xnodes = parent._xnodes,\r
559                         l      = xnodes.length,\r
560                         i      = 0,\r
561                         child;\r
562                 for( ; i < l; ++i ){\r
563                         child = xnodes[ i ];\r
564                         if( child._tag ){\r
565                                 ( !tag || child._tag === tag ) && ( list[ list.length ] = child );\r
566                                 //console.log( parent._tag + ' > ' + child._tag + ' == ' + tag+ ' l:' + list.length );\r
567                                 child._xnodes && child._xnodes.length && X_Node_Selector__fetchElements( list, child, tag );\r
568                         };\r
569                 };\r
570         };\r
571 \r
572         function X_Node_Selector__funcSelectorChild( type, flag_all, flags, xnodes ){\r
573                 var res      = [],\r
574                         flag_not = flags.not,\r
575                         i = 0, n = -1, xnode, node,\r
576                         tagName, tmp;\r
577                 for( ; xnode = xnodes[ i ]; ++i ){\r
578                         tagName = flag_all || xnode._tag;\r
579                         tmp     = null;\r
580                         if( /* tmp === null && */ type <= 0 ){\r
581                                 for( node = xnode.prev(); node; node = node.prev() ){\r
582                                         if( node._tag && ( flag_all || tagName === node._tag ) ){\r
583                                                 tmp = false;\r
584                                                 break;\r
585                                         };\r
586                                 };\r
587                         };\r
588                         if( tmp === null && 0 <= type ){\r
589                                 for( node = xnode.next(); node; node = node.next() ){\r
590                                         if( node._tag && ( flag_all || tagName === node._tag ) ){\r
591                                                 tmp = false;\r
592                                                 break;\r
593                                         };              \r
594                                 };                                              \r
595                         };\r
596                         if( tmp === null ) tmp = true;\r
597                         if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
598                 };\r
599                 return res;\r
600         };\r
601         function X_Node_Selector__funcSelectorNth( pointer, sibling, flag_all, flags, xnodes, a, b ){\r
602                 var _data    = funcData,\r
603                         res      = [],\r
604                         checked  = {},\r
605                         flag_not = flags.not,\r
606                         i = 0, n = -1, uid,\r
607                         c, xnode, tmp, node, tagName;\r
608                 for( ; xnode = xnodes[ i ]; ++i ){\r
609                         uid = xnode._uid;\r
610                         tmp = checked[ uid ];\r
611                         if( tmp === void 0 ){\r
612                                 for( c = 0, node = xnode.parent[ pointer ](), tagName = flag_all || xnode._tag; node; node = node[ sibling ]() ){\r
613                                         if( node._tag && ( flag_all || tagName === node._tag ) ){\r
614                                                 ++c;\r
615                                                 checked[ node._uid ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;\r
616                                         };                                                      \r
617                                 };\r
618                                 tmp = checked[ uid ];\r
619                         };\r
620                         if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
621                 };\r
622                 return res;\r
623         };\r
624         function X_Node_Selector__funcSelectorProp( prop, flag, flags, xnodes ){\r
625                 var res = [],\r
626                         flag_not = flag ? flags.not : !flags.not,\r
627                         i = 0, n = -1, xnode;\r
628                 for( ; xnode = xnodes[ i ]; ++i ){\r
629                         if( xnode._attrs && xnode._attrs[ prop ] ^ flag_not ) res[ ++n ] = xnode;\r
630                 };\r
631                 return res;\r
632         };\r
633 \r
634 var X_Node_Selector__filter = {\r
635         root : function( elem ){\r
636                 return elem === ( elem.ownerDocument || elem.document ).documentElement;\r
637         },\r
638         target : {\r
639                 m : function( flags, xnodes ){\r
640                         var res  = [],\r
641                                 hash = location.hash.slice( 1 ),\r
642                                 flag_not = flags.not,\r
643                                 i = 0, n = -1, xnode;\r
644                         for ( ; xnode = xnodes[ i ]; ++i ){\r
645                                 if( ( ( xnode._id || xnode._attrs && xnode._attrs.name ) === hash ) ^ flag_not ) res[ ++n ] = xnode;                                            \r
646                         };\r
647                         return res;\r
648                 }\r
649         },\r
650         'first-child' : {\r
651                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( -1, true, flags, xnodes ); }\r
652         },\r
653         'last-child' : {\r
654                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 1, true, flags, xnodes ); }\r
655         },\r
656         'only-child' : {\r
657                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 0, true, flags, xnodes ); }\r
658         },\r
659         'first-of-type' : {\r
660                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( -1, false, flags, xnodes ); }\r
661         },\r
662         'last-of-type' : {\r
663                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 1, false, flags, xnodes ); }\r
664         },\r
665         'only-of-type' : {\r
666                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 0, false, flags, xnodes ); }\r
667         },\r
668         'nth-child' : {\r
669                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'firstChild', 'next', true, flags, xnodes, a, b ); }\r
670         },\r
671         'nth-last-child' : {\r
672                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'lastChild', 'prev', true, flags, xnodes, a, b ); }\r
673         },\r
674         'nth-of-type' : {\r
675                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'firstChild', 'next', false, flags, xnodes, a, b ); }\r
676         },\r
677         'nth-last-of-type' : {\r
678                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'lastChild', 'prev', false, flags, xnodes, a, b ); }\r
679         },\r
680         empty : {\r
681                 m : function( flags, xnodes ){\r
682                         var res = [],\r
683                                 flag_not = flags.not,\r
684                                 i = 0, n = -1, xnode, tmp, node;\r
685                         for( ; xnode = xnodes[i]; ++i ){\r
686                                 tmp = true;\r
687                                 for( node = xnode.firstChild(); node; node = node.nextSibling() ){\r
688                                         if( node._tag || ( !node._tag && node._text ) ){\r
689                                                 tmp = false;\r
690                                                 break;\r
691                                         };                              \r
692                                 };\r
693                                 if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
694                         };\r
695                         return res;\r
696                 }\r
697         },\r
698         link : {\r
699                 m : function( flags, xnodes ){\r
700                         var links = ( xnodes[ 0 ].ownerDocument || xnodes[ 0 ].document ).links,\r
701                                 _data = funcData,\r
702                                 res   = [],\r
703                                 checked, flag_not, i, link, xnode, n;\r
704                         if( !links ) return res;\r
705                         checked = {};\r
706                         flag_not = flags.not;\r
707                         for( i = 0; link = links[ i ]; ++i ){\r
708                                 checked[ ( new Node( link ) )._uid ] = true;\r
709                         };\r
710                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
711                                 if( checked[ xnode._uid ] ^ flag_not ) res[ ++n ] = xnode;\r
712                         };\r
713                         return res;\r
714                 }\r
715         },\r
716         lang : {\r
717                 m : function( flags, xnodes, arg ){\r
718                         var res = [],\r
719                                 //reg = new RegExp('^' + arg, 'i'),\r
720                                 flag_not = flags.not,\r
721                                 i = 0, n = -1, xnode, tmp, lang;\r
722                         arg = arg.toLowerCase();\r
723                         for( ; tmp = xnode = xnodes[ i ]; ++i ){\r
724                                 while( tmp && !( lang = tmp._attrs && tmp._attrs[ 'lang' ] ) ){\r
725                                         tmp = tmp.parent;\r
726                                 };\r
727                                 //tmp = !!(tmp && reg.test(tmp.getAttribute('lang')));\r
728                                 if( ( !!lang && lang.toLowerCase().indexOf( arg ) === 0 ) ^ flag_not ) res[ ++n ] = xnode;\r
729                         };\r
730                         return res;\r
731                 }\r
732         },\r
733         enabled : {\r
734                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'disabled', false, flags, xnodes ); }\r
735         },\r
736         disabled : {\r
737                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'disabled', true, flags, xnodes ); }\r
738         },\r
739         checked : {\r
740                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'checked', true, flags, xnodes ); }\r
741         },\r
742         contains : {\r
743                 m : function( flags, xnodes, arg ){\r
744                         var res = [],\r
745                                 flag_not = flags.not,\r
746                                 i = 0, n = -1, xnode;\r
747                         for( ; xnode = xnodes[ i ]; ++i ){\r
748                                 if ( ( -1 < ( xnode.text() ).indexOf( arg ) ) ^ flag_not ) res[ ++n ] = xnode;                                          \r
749                         };\r
750                         return res;\r
751                 }\r
752         }\r
753 };\r