OSDN Git Service

727ac95ab0852a9e28361c6fd476987602388709
[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 /*\r
61  * セレクタ文字列の解析、但し一挙に行わず、ひと塊づつ\r
62  * 結合子 + 単体セレクタ( タグ,*,#,.,[],: )\r
63  * ' ' 子孫セレクタ, '>' 直下,'+','~' の場合、tagName|* を返す\r
64  * return [ 今回のパースで解析した終端位置, [ selector, ... ] ] error: return pointer\r
65  */\r
66 function X_Node_Selector__parse( query, last ){\r
67         var COMBINATOR = X_Node_Selector__COMBINATOR,\r
68                 SELECTOR   = X_Node_Selector__SELECTOR,\r
69                 OPERATORS  = X_Node_Selector__OPERATORS,\r
70                 ALPHABET   = X_Node_Selector__ALPHABET,\r
71                 NUMBER     = X_Node_Selector__NUMBER,\r
72                 result     = [],\r
73                 i          = -1,\r
74                 l          = query.length,\r
75                 phase      = 0x0,\r
76                 combinator = 0,\r
77                 selector   = 0,\r
78                 chr, chrCode, nameChr, name1st,\r
79                 tmp, escape, quot, start,\r
80                 name, key, value, operator, a, b, not;\r
81 \r
82         query += ' ';\r
83         while( i < l ){\r
84                 chr     = query.charAt( ++i );\r
85                 chrCode = ALPHABET.indexOf( chr ); // TODO この関数無くす!\r
86                 nameChr = chrCode !== -1;\r
87                 name1st = nameChr && chrCode < 52;\r
88                 switch( phase ){\r
89                         case 0x0 :\r
90                                 name1st ? // tagName\r
91                                         ( ( selector = 1 ) && ( phase = 0x2 ) && ( start = i ) ) :\r
92                                 !not && ( tmp = COMBINATOR[ chr ] ) ? (\r
93                                         ( 1 < tmp && 1 < combinator ) ?\r
94                                                 ( phase = 0xf ) :\r
95                                                 ( phase = tmp === 5 ? 0xe : 0x0 ) & ( ( 1 < tmp || combinator < 1 ) && ( combinator = tmp ) ) & ( tmp === 5 && ++i ) ) : // ' ' でない結合子の上書きはエラー\r
96                                 ( tmp = SELECTOR[ chr ] ) ? // [\r
97                                         ( selector = tmp ) && ( phase = selector === 5 ? 0x4 : 0x1 ) : // 7:[, 0<:\r
98                                 chr === '*' ?\r
99                                         ( ( selector = 1 ) && ( name = chr ) && ( phase = 0xe ) && ++i ) :\r
100                                         chr !== ' ' && ( phase = 0xf );\r
101                                 //console.log( '0x0: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase + ' tmp:' + tmp + ' comb:' + combinator );\r
102                                 break;\r
103                         case 0x1 :\r
104                                 name1st ?\r
105                                         ( ( start = i ) && ( phase = 0x2 ) ) :\r
106                                         chr !== ' ' && ( phase = 0xf );\r
107                                 break;\r
108                         case 0x2 :\r
109                                 !nameChr && !( escape && ( selector === 2 || selector === 3 ) && ( chr === ':' || chr === '.' ) ) ? // id or class の場合 : . を直前にエスケープした場合に限り使える\r
110                                         ( name = query.substring( start, i ) ) && ( phase = selector === 4 && name !== 'not' && chr === '(' ? ( name !== 'lang' && name !== 'contains' ? 0x8 : 0xb ) : 0xe ) :\r
111                                 SELECTOR[ chr ] < 4 && ( phase = 0xe );\r
112                                 break;\r
113                         \r
114                         case 0x3 : //:nth-\r
115                                 break;\r
116                         \r
117                         case 0x4 : // start attr filter\r
118                                 name1st ?\r
119                                         ( ( phase = 0x5 ) && ( start = i ) ) :\r
120                                 chr !== ' ' &&\r
121                                         ( phase = 0xf );\r
122                                 break;\r
123                         case 0x5 : // attr filter key\r
124                                 chr === '=' ?\r
125                                         ( ( operator = 1 ) && ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = i + 1 ) ) :\r
126                                 chr === ']' ?\r
127                                         ( !( operator = 0 ) && ( phase = 0xe ) && ( key || ( key = query.substring( start, i ) ) ) && ++i ) :\r
128                                 chr === ' ' ?\r
129                                         ( key || ( key = query.substring( start, i ) ) ) :\r
130                                 ( operator = OPERATORS[ query.substr( i, 2 ) ] ) ?\r
131                                         ( ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = ++i ) ) :\r
132                                         !nameChr && ( phase = 0xf );\r
133                                 //console.log( name1st + ' ' + chrCode + chr + phase );\r
134                                 break;\r
135                         case 0x6 :\r
136                                 ( chr === '"' || chr === "'" ) && !escape && !quot ?\r
137                                         ( quot = chr ) && ( start = i + 1 ) && ( phase = 0x7 ) :\r
138                                         chr !== ' ' && ( start = i ) && ( phase = 0x7 );\r
139                                 break;\r
140                                 \r
141                         case 0x7 : // attr filter value\r
142                                 chr === quot ?\r
143                                         !escape && !value && ( value = query.substring( start, i ) ) :\r
144                                 chr === ']' ?\r
145                                         ( ( value || ( value = query.substring( start, i ) ) ) && ( phase = 0xe ) && ++i ) :    \r
146                                 chr === ' ' && !quot && !value && ( value = query.substring( start, i ) );                      \r
147                                         //( chr === '"' || chr === "'" ) && !quot && ( quot = chr ) && ( start = i + 1 );\r
148                                 break;\r
149                                 \r
150                         case 0x8 : // 4, 2n, even, odd, -n+4,\r
151                                 NUMBER.indexOf( chr ) !== -1 ?\r
152                                         ( start = i ) && ( phase = 0x9 ) :\r
153                                 chr === 'n' ?\r
154                                         ( phase = 0xa ) && ( a = 1 ) && ( start = i + 1 ) :\r
155                                 query.substr( i, 4 ) === 'even' ?\r
156                                         ( ( a = 2 ) && !( b = 0 ) && ( i += 3 ) ) :\r
157                                 query.substr( i, 3 ) === 'odd' ?\r
158                                         ( ( a = 2 ) && ( b = 1 ) && ( i += 2 ) ) :                                      \r
159                                 chr === ')' && ( phase = a ? 0xe : 0xf ) && ++i;\r
160                                 //console.log( '0x8: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase + ' ' + name );\r
161                                 break;\r
162                         case 0x9 :\r
163                                 tmp = query.substring( start, i );\r
164                                 chr === 'n' ?\r
165                                         ( phase = 0xa ) && ( start = i + 1 ) && ( a = tmp === '+' ? 1 : tmp === '-' ? -1 : parseFloat( tmp ) ) :\r
166                                 chr === ')' && ( phase = 0xe ) && ++i && ( b = parseFloat( tmp ) ) && ( a = 0 );\r
167                                 //console.log( '0x9: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase );\r
168                                 break;\r
169                         case 0xa :\r
170                                 chr === ')' && ( phase = 0xe ) && ++i && ( b = parseFloat( query.substring( start, i ) ) || 0 );\r
171                                 //console.log( '0xa: ' + start + ' ' + i );\r
172                                 break;\r
173                         // contains, lang\r
174                         case 0xb :\r
175                                 ( chr === '"' || chr === "'" ) && !escape && !quot &&\r
176                                         ( quot = chr ) && ( start = i + 1 ) && ( phase = 0xc );\r
177                                 break;  \r
178                         case 0xc :\r
179                                 chr === quot && !escape && ( value = query.substring( start, i ) ) && ( phase = 0xd );\r
180                                 break;\r
181                         case 0xd :\r
182                                 chr === ')' && ( phase = 0xe ) && ++i;                          \r
183                                 break;\r
184                         default :\r
185                 };\r
186                 \r
187                 if( phase === 0xf ) return i;\r
188                 \r
189                 //alert( chr + ' ' + phase + ' ' + selector + ' ' + name + ' ' + name1st )\r
190                 if( phase === 0xe ){\r
191                         if( selector === 4 ){// :not\r
192                                 if( name === 'not' ){\r
193                                         if( not ){\r
194                                                 return i; // error\r
195                                         } else {\r
196                                                 not      = true;\r
197                                                 selector = 0;\r
198                                                 phase    = 0x0;\r
199                                                 name     = null;                                        \r
200                                         };\r
201                                         //continue;\r
202                                 } else\r
203                                 if( name === 'lang' || name === 'contains' ){\r
204                                         result = [ not ? 0 : combinator, selector, name, value ];\r
205                                         break;\r
206                                 } else {\r
207                                         if( a !== a || b !== b ) return i;\r
208                                         result = [ not ? 0 : combinator, SELECTOR[ name ] || selector, name, a, b ];\r
209                                         break;  \r
210                                 };\r
211                         } else {\r
212                                 result =\r
213                                         combinator === 5 ?\r
214                                                 5 :\r
215                                         selector === 5 ?\r
216                                                 [ not ? 0 : combinator, selector, key, operator, value ] :\r
217                                                 [ not ? 0 : combinator, selector, name.split( '\\' ).join( '' ) ];\r
218                                 break;                          \r
219                         };\r
220                 };\r
221 \r
222                 escape = chr === '\\' && !escape;\r
223         };\r
224         //if( phase !== 0xe ) return i;\r
225         if( not && ( tmp = query.substr( i ).indexOf( ')' ) ) === -1 ) return i;\r
226         return not ? [ i + tmp + 1, [ combinator, 6, result ] ] : [ i, result ];\r
227 };\r
228 \r
229         /**\r
230          * selector を使って Node, NodeList を取得する\r
231          * @alias X.Doc.find\r
232          * @function\r
233          * @param {string} セレクター文字列\r
234          * @return {Node|NodeList}\r
235          */\r
236         X[ 'Doc' ][ 'find' ] = X_shortcutFunction =\r
237 \r
238         /**\r
239          * selector を使って Node, NodeList を取得する\r
240          * @alias NodeList.prototype.find\r
241          * @function\r
242          * @param {string} セレクター文字列\r
243          * @return {Node|NodeList}\r
244          */\r
245         X_NodeList.prototype[ 'find' ] = X_Node_find;\r
246         \r
247         /**\r
248          * selector を使って Node, NodeList を取得する\r
249          * @alias Node.prototype.find\r
250          * @function\r
251          * @param {string} セレクター文字列\r
252          * @return {Node|NodeList}\r
253          */     \r
254         function X_Node_find( queryString ){\r
255                 var HTML      = X_Node_html,\r
256                         scope     = this.constructor === X_NodeList && this.length ? this : [ this.constructor === Node ? this : X_Node_body ],\r
257                         parents   = scope, // 探索元の親要素 XNodeList の場合あり\r
258                         // TODO { title : true,,, }\r
259                         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
260                         ARY_PUSH  = Array.prototype.push,\r
261                         ret       = [], // 結果要素\r
262                         root      = X_Node_getRoot( scope[ 0 ] ),\r
263                         isXML     = !!X_Node_isXmlDocument( root ),\r
264                         isMulti   = 1 < scope.length,// 要素をマージする必要がある\r
265                         isStart   = true,\r
266                         _         = ' ',\r
267                         isAll, isNot, hasRoot,\r
268                         l, i, n, parsed,\r
269                         xnodes, // 一時保存用\r
270                         merge, // 要素がコメントノードで汚染されている場合使う\r
271                         parent, children, j, m,\r
272                         combinator, selector, name, tagName,\r
273                         uid, tmp, xnode, filter, key, op, val, toLower, useName,\r
274             links, className, attr, flag;\r
275 \r
276                 /*@+debug[*/\r
277                 if( X_ViewPort_readyState < X_EVENT_XDOM_READY ){\r
278                         alert( 'not ready! use X.ViewPort.listenOnce( X_EVENT_XDOM_READY, callback )' );\r
279                         return;\r
280                 };\r
281                 /*]@+debug*/\r
282 \r
283                 // 文字列以外は空で返す\r
284                 if( typeof queryString !== 'string' ) return ret;\r
285                 \r
286                 xnodes = [];\r
287                 \r
288                 // 以下、パースと探索\r
289                 for( ; queryString.length; ){\r
290                         //console.log( 'queryString[' + queryString + ']' );\r
291                         \r
292                         // 初期化処理\r
293                         if( !parsed ){\r
294                                 parsed = X_Node_Selector__parse( queryString );\r
295                                 \r
296                                 if( typeof parsed === 'number' ){\r
297                                         // error\r
298                                         return [];\r
299                                 };\r
300                                 \r
301                                 queryString = queryString.substr( parsed[ 0 ] );\r
302                                 parsed      = parsed[ 1 ];\r
303                                 \r
304                                 //console.log( 'X_Node_Selector__parse ' + parsed );\r
305                                 \r
306                                 if( parsed === 5 ){\r
307                                         isMulti = true;\r
308                                         parents = scope;\r
309                                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
310                                         parsed  = null;\r
311                                         xnodes  = [];\r
312                                         isStart = true;\r
313                                         continue;\r
314                                 };\r
315                         };\r
316                         \r
317                         combinator  = parsed[ 0 ];\r
318                         selector    = parsed[ 1 ];\r
319                         name        = parsed[ 2 ];\r
320                         tagName     = selector === 1 ? ( isXML ? name : name.toUpperCase() ) : '*';\r
321                         isAll       = tagName === '*';\r
322         \r
323                         if( !isStart ){\r
324                                 if( !xnodes.length ){\r
325                                         parsed = null;\r
326                                         continue;                                       \r
327                                 } else\r
328                                 if( combinator !== 0 ){\r
329                                         parents = xnodes;\r
330                                         xnodes  = [];\r
331                                         //console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xnodes.length );\r
332                                 };\r
333                         };\r
334                         \r
335                         i = 0;\r
336                         l = parents.length;\r
337                         n = -1; \r
338                         isMulti = isMulti || 1 < l;\r
339                         \r
340                         //console.log( 'combinator ' + combinator );\r
341         \r
342                         switch( combinator ){\r
343                                 // > TagName|*\r
344                                 case 2 :\r
345                                         for( ; i < l; ++i ){\r
346                                                 parent = parents[ i ];\r
347                                                 if( ( children = parent[ '_xnodes' ] ) && ( m = children.length ) ){\r
348                                                         for( j = 0; j < m; ++j ){\r
349                                                                 xnode = children[ j ];\r
350                                                                 if( xnode[ '_tag' ] && ( isAll || tagName === xnode[ '_tag' ] ) ) xnodes[ ++n ] = xnode;\r
351                                                         };\r
352                                                 };                      \r
353                                         };\r
354                                         break;\r
355                                 // + TagName|*\r
356                                 case 3 :\r
357                                         for( ; i < l; ++i ){\r
358                                                 \r
359                                                 xnode = parents[ i ];\r
360                                                 j = xnode[ 'getOrder' ]() + 1;\r
361                                                 parent = xnode.parent;\r
362                                                 if( parent && ( children = parent[ '_xnodes' ] ) && ( m = children.length ) ){\r
363                                                         for( ; j < m; ++j ){\r
364                                                                 xnode = children[ j ];\r
365                                                                 if( xnode[ '_tag' ] ){\r
366                                                                         if( isAll || tagName === xnode[ '_tag' ] ){\r
367                                                                                 xnodes[ ++n ] = xnode;\r
368                                                                         };\r
369                                                                         break;\r
370                                                                 };\r
371                                                         };\r
372                                                 };\r
373                                                 /*\r
374                                                 for( xnode = parents[ i ][ 'next' ](); xnode; xnode = xnode[ 'next' ]() ){\r
375                                                         if( xnode[ '_tag' ] ){\r
376                                                                 if( isAll || tagName === xnode[ '_tag' ] ) xnodes[ ++n ] = xnode;\r
377                                                                 break;\r
378                                                         };                                                                      \r
379                                                 };      */                                                      \r
380                                         };\r
381                                         break;\r
382                                 // ~ TagName|*\r
383                                 case 4 :\r
384                                         merge  = {};\r
385                                         for( ; i < l; ++i ){\r
386                                                 for( xnode = parents[ i ][ 'next' ](); xnode; xnode = xnode[ 'next' ]() ){\r
387                                                         if( xnode[ '_tag' ] && ( isAll || tagName === xnode[ '_tag' ] ) ){\r
388                                                                 uid = xnode[ '_uid' ];\r
389                                                                 if( merge[ uid ] ){\r
390                                                                         break;\r
391                                                                 } else {\r
392                                                                         merge[ uid ] = true;\r
393                                                                         xnodes[ ++n ] = xnode;\r
394                                                                 };\r
395                                                         };                                                                      \r
396                                                 };                                                              \r
397                                         };\r
398                                         break;\r
399                                 \r
400                                 // case 6 : 属性ノードは実装しない\r
401                                 \r
402                                 default :                               \r
403                                         if( combinator === 1 || ( isStart && selector < 7 ) ){\r
404                                                 if( isStart ){\r
405                                                         if( tagName === 'HTML' ){\r
406                                                                 xnodes[ 0 ] = X_Node_html;\r
407                                                                 break;\r
408                                                         };\r
409                                                         if( tagName === 'HEAD' ){\r
410                                                                 xnodes[ 0 ] = X_Node_head;\r
411                                                                 break;\r
412                                                         };\r
413                                                         if( tagName === 'BODY' ){\r
414                                                                 xnodes[ 0 ] = X_Node_body;\r
415                                                                 break;\r
416                                                         };\r
417                                                         \r
418                                                 };\r
419                                                 //console.log( l + ' > ' + xnodes.length + ' tag:' + tagName );\r
420                                                 merge = {};\r
421                                                 X_Node_Selector__fetchElements( xnodes, parents, isAll ? '' : tagName, merge );\r
422                                                 //console.log( l + ' >> ' + xnodes.length + ' tag:' + tagName );\r
423                                         };\r
424                         };\r
425                         \r
426                         isStart = false;\r
427                         \r
428                         //alert( 'pre-selector:' + ( xnodes && xnodes.length ) )\r
429                         \r
430                         switch( selector ){\r
431                                 // #, ID\r
432                                 case 2 :\r
433                                         filter = [ 'id', 1, name ]; break;\r
434                                 // ., class\r
435                                 case 3 :\r
436                                         filter = [ 'class', 3 /*'~='*/, name ]; break;\r
437                                 // :, 擬似クラス\r
438                                 case 4 :\r
439                                         if( !( filter = X_Node_Selector__filter[ name ] ) ){\r
440                                                 return [];\r
441                                         };\r
442                                         break;\r
443                                 // [] 属性\r
444                                 case 5 :\r
445                                         filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
446                                 // :not\r
447                                 case 6 :\r
448                                         isNot  = true;\r
449                                         parsed = parsed[ 2 ];\r
450                                         name   = parsed[ 2 ];\r
451                                         switch( parsed[ 1 ] ) {\r
452                                                 case 1 :\r
453                                                         filter = [ 'tag', 1, isXML ? name : name.toUpperCase() ]; break;\r
454                                                 // #, ID\r
455                                                 case 2 :\r
456                                                         filter = [ 'id', 1, name ]; break;\r
457                                                 // ., class\r
458                                                 case 3 :\r
459                                                         filter = [ 'class', 3, name ]; break;\r
460                                                 // :, 擬似クラス\r
461                                                 case 4 :\r
462                                                         if( !( filter = X_Node_Selector__filter[ name ] ) ){\r
463                                                                 return [];\r
464                                                         };\r
465                                                         break;\r
466                                                 // [] 属性\r
467                                                 case 5 :\r
468                                                         filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
469                                         };\r
470                                         break;\r
471                                 // scope\r
472                                 case 7 :\r
473                                         xnodes = scope; break;\r
474                                 // root\r
475                                 case 8 :\r
476                                         hasRoot = true;\r
477                                         xnodes = [ HTML ]; break;\r
478                                 // link\r
479                                 case 9 :\r
480                                         if( links = document.links ){\r
481                                                 for( xnodes = [], i = links.length; i; ){\r
482                                                         xnodes[ --i ] = Node( links[ i ] );\r
483                                                 };\r
484                                         } else {\r
485                                                 // area[href],a[href]\r
486                                         };\r
487                         };\r
488                         \r
489                         if( filter && xnodes.length ){\r
490                                 // filter.mが関数の場合\r
491                                 if( filter.m ){\r
492                                         xnodes = filter.m(\r
493                                                 {\r
494                                                         not : isNot,\r
495                                                         xml : isXML\r
496                                                 },\r
497                                                 xnodes,\r
498                                                 parsed[ 3 ], parsed[ 4 ]\r
499                                         );\r
500                                 } else\r
501                                 // filterが関数の場合\r
502                                 if( typeof filter === 'function' ){\r
503                                         tmp = [];\r
504                                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
505                                                 if( ( !!filter( xnode ) ) ^ isNot ) tmp[ ++n ] = xnode; \r
506                                         };\r
507                                         xnodes = tmp;\r
508                                 } else {\r
509                                 // 属性セレクター                        \r
510                                         tmp = [];\r
511                                         key = filter[ 0 ];\r
512                                         op  = filter[ 1 ];\r
513                                         val = filter[ 2 ];\r
514                                         \r
515                                         key = X_Node_Attr_renameForTag[ key ] || key;\r
516                                         \r
517                                         // [class~='val']\r
518                                         if( !isXML && key === 'class' && op === 3 ){\r
519                                                 val = _ + val + _;\r
520                                                 for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
521                                                         className = xnode[ '_className' ];\r
522                                                         if( !!( className && ( _ + className + _ ).indexOf( val ) > -1 ) ^ isNot ) tmp[ ++n ] = xnode;\r
523                                                 };\r
524                                         } else {\r
525                                         // 通常\r
526                                                 // 諦めて、funcAttrを呼ぶ\r
527                                                 // flag_call  = ($.browser.safari && key === 'selected');\r
528                                                 // getAttributeを使わない\r
529                                                 useName = X_UA[ 'IE' ] && key !== 'href' && key !== 'src';\r
530                                                 toLower = !!val && !isXML && noLower.indexOf( key ) === -1; //!noLower.test(key);\r
531                                                 if( toLower ) val = val.toLowerCase();\r
532                                                 if( op === 3 ) val = _ + val + _;\r
533 \r
534                                                 for( i = 0, n = -1, l = xnodes.length; i < l; ++i ){\r
535                                                         xnode = xnodes[ i ];\r
536                                                         attr =\r
537                                                                 key === 'tag' ? xnode[ '_tag' ] :\r
538                                                                 key === 'id' ? xnode[ '_id' ] :\r
539                                                                 key === 'class' ? xnode[ '_className' ] :\r
540                                                                 xnode[ '_attrs' ] && xnode[ '_attrs' ][ key ];\r
541                                                                 //flag_call ?\r
542                                                                 //      funcAttr( elem, key ) :\r
543                                                                 //useName ?\r
544                                                                 //      elem[ X_Node_Attr_renameForDOM[ key ] || key ] :\r
545                                                                 //      elem.getAttribute( key, 2 );\r
546                                                         flag = !!attr;// && ( !useName || attr !== '' );\r
547                                                         if( flag && op ){\r
548                                                                 if( toLower ) attr = attr.toLowerCase();\r
549                                                                 \r
550                                                                 switch( op ){\r
551                                                                         case 1: // =\r
552                                                                                 flag = attr === val;\r
553                                                                                 break;\r
554                                                                         case 2: // !=\r
555                                                                                 flag = attr !== val;\r
556                                                                                 break;\r
557                                                                         case 3: // ~=\r
558                                                                                 flag = ( _ + attr + _ ).indexOf( val ) !== -1;\r
559                                                                                 break;\r
560                                                                         case 4: // ^=\r
561                                                                                 flag = attr.indexOf( val ) === 0;\r
562                                                                                 break;\r
563                                                                         case 5: // $=\r
564                                                                                 flag = attr.lastIndexOf( val ) + val.length === attr.length;\r
565                                                                                 break;\r
566                                                                         case 6: // *=\r
567                                                                                 flag = attr.indexOf( val ) !== -1;\r
568                                                                                 break;\r
569                                                                         case 7: // |=\r
570                                                                                 flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';\r
571                                                                                 break;\r
572                                                                 };\r
573                                                         };\r
574                                                         if( !!flag ^ isNot ) tmp[ ++n ] = xnode;\r
575                                                 };\r
576                                         };\r
577                                         xnodes = tmp;\r
578                                 };\r
579                         };\r
580                         filter  = null;\r
581                         isNot   = false;\r
582                         parsed  = null;\r
583                         \r
584                         //console.log( '//end :' + ( xnodes && xnodes.length ) );\r
585                 };\r
586                 //console.log( 'multi:' + ( xnodes && xnodes.length ) );\r
587                 \r
588                 // tree 順に並び替え、同一要素の排除\r
589                 if( isMulti ){\r
590                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
591                         l = ret.length;\r
592                         if( l < 2 ) return ret[ 0 ] || X_Node_none;\r
593                         \r
594                         xnodes = [];\r
595                         merge  = {};\r
596                         for( i = 0, n = -1; i < l; ++i ){\r
597                                 //alert( 'multi:' + i )\r
598                                 xnode = ret[ i ];\r
599                                 if( !merge[ uid = xnode[ '_uid' ] ] ){\r
600                                         merge[ uid ] = true;\r
601                                         xnodes[ ++n ] = xnode;\r
602                                 };\r
603                         };\r
604                         xnodes = X_Node_Selector__sortElementOrder( [], xnodes, hasRoot ? [ HTML ] : HTML[ '_xnodes' ] );\r
605                 };\r
606 \r
607                 return xnodes.length === 1 ? xnodes[ 0 ] : new X_NodeList( xnodes );\r
608         };\r
609         \r
610         function X_Node_Selector__sortElementOrder( newList, list, xnodes ){\r
611                 var l = xnodes.length,\r
612                         i = 0,\r
613                         j, child, _xnodes;\r
614                 for( ; i < l; ++i ){\r
615                         child = xnodes[ i ];\r
616                         if( !child[ '_tag' ] ) continue;\r
617 \r
618                         j = list.indexOf( child );\r
619                         if( j !== -1 ){\r
620                                 newList[ newList.length ] = child;\r
621                                 if( list.length === 2 ){\r
622                                         newList[ newList.length ] = list[ j === 0 ? 1 : 0 ];\r
623                                         return newList;\r
624                                 };\r
625                                 list.splice( j, 1 );\r
626                         };\r
627 \r
628                         if( ( _xnodes = child[ '_xnodes' ] ) && X_Node_Selector__sortElementOrder( newList, list, _xnodes ) ){\r
629                                 return newList;\r
630                         };\r
631                 };\r
632         };\r
633 \r
634         function X_Node_Selector__fetchElements( list, xnodes, tag, merge ){\r
635                 var l      = xnodes.length,\r
636                         i      = 0,\r
637                         child, uid, _tag, _xnodes;\r
638                 for( ; i < l; ++i ){\r
639                         child = xnodes[ i ];\r
640                         uid   = child[ '_uid' ];\r
641                         _tag  = child[ '_tag' ];\r
642                         if( !merge[ uid ] && _tag ){\r
643                                 merge[ uid ] = true;\r
644                                 ( !tag || tag === _tag ) && ( list[ list.length ] = child );\r
645                                 if( ( _xnodes = child[ '_xnodes' ] ) && ( 1 < _xnodes.length || ( _xnodes[ 0 ] && _xnodes[ 0 ][ '_tag' ] ) ) ){\r
646                                         X_Node_Selector__fetchElements( list, _xnodes, tag, merge );\r
647                                 };\r
648                         };\r
649                 };\r
650         };\r
651 /*\r
652         function X_Node_Selector__fetchElements( list, parent, tag, merge ){\r
653                 \r
654                 var xnodes   = parent[ '_xnodes' ],\r
655                         memory   = {\r
656                                 i      : 0,\r
657                                 l      : xnodes.length,\r
658                                 xnodes : xnodes\r
659                         },              \r
660                         memories = [ memory ],\r
661                         i, l, xnode,\r
662                         uid, _xnodes;\r
663                 \r
664                 while( memories.length ){\r
665                         memory = memories.pop();\r
666                         xnodes = memory.xnodes;\r
667                         i      = memory.i;\r
668                         l      = memory.l;\r
669                         for( ; i < l; ++i ){\r
670                                 xnode = xnodes[ i ];\r
671                                 uid   = xnode[ '_uid' ];\r
672                                 if( !merge[ uid ] && xnode[ '_tag' ] ){\r
673                                         if( !tag || xnode[ '_tag' ] === tag ) list[ list.length ] = xnode;\r
674                                         \r
675                                         if( _xnodes = xnode[ '_xnodes' ] ){\r
676                                                 if( 1 < _xnodes.length || ( _xnodes[ 0 ] && _xnodes[ 0 ][ '_tag' ] ) ){\r
677                                                         memory.i = i + 1;\r
678                                                         memory.l = l;\r
679                                                         memory.xnodes = xnodes;\r
680                                                         memories[ memories.length ] = memory;\r
681                                                         memories[ memories.length ] = {\r
682                                                                 i      : 0,\r
683                                                                 l      : _xnodes.length,\r
684                                                                 xnodes : _xnodes\r
685                                                         };\r
686                                                         merge[ uid ] = true;\r
687                                                         break;\r
688                                                 };\r
689                                         };\r
690                                 };\r
691                                 merge[ uid ] = true;\r
692                         };\r
693                 };\r
694         };\r
695  */\r
696         function X_Node_Selector__funcSelectorChild( type, flag_all, flags, xnodes ){\r
697                 var res      = [],\r
698                         flag_not = flags.not,\r
699                         i = 0, n = -1, xnode, node,\r
700                         tagName, tmp;\r
701                 for( ; xnode = xnodes[ i ]; ++i ){\r
702                         tagName = flag_all || xnode[ '_tag' ];\r
703                         tmp     = null;\r
704                         if( /* tmp === null && */ type <= 0 ){\r
705                                 for( node = xnode[ 'prev' ](); node; node = node[ 'prev' ]() ){\r
706                                         if( node[ '_tag' ] && ( flag_all || tagName === node[ '_tag' ] ) ){\r
707                                                 tmp = false;\r
708                                                 break;\r
709                                         };\r
710                                 };\r
711                         };\r
712                         if( tmp === null && 0 <= type ){\r
713                                 for( node = xnode[ 'next' ](); node; node = node[ 'next' ]() ){\r
714                                         if( node[ '_tag' ] && ( flag_all || tagName === node[ '_tag' ] ) ){\r
715                                                 tmp = false;\r
716                                                 break;\r
717                                         };              \r
718                                 };                                              \r
719                         };\r
720                         if( tmp === null ) tmp = true;\r
721                         if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
722                 };\r
723                 return res;\r
724         };\r
725         function X_Node_Selector__funcSelectorNth( pointer, sibling, flag_all, flags, xnodes, a, b ){\r
726                 var res      = [],\r
727                         checked  = {},\r
728                         flag_not = flags.not,\r
729                         i = 0, n = -1, uid,\r
730                         c, xnode, tmp, node, tagName;\r
731                 for( ; xnode = xnodes[ i ]; ++i ){\r
732                         uid = xnode[ '_uid' ];\r
733                         tmp = checked[ uid ];\r
734                         if( tmp === void 0 ){\r
735                                 for( c = 0, node = xnode.parent[ pointer ](), tagName = flag_all || xnode[ '_tag' ]; node; node = node[ sibling ]() ){\r
736                                         if( node[ '_tag' ] && ( flag_all || tagName === node[ '_tag' ] ) ){\r
737                                                 ++c;\r
738                                                 checked[ node[ '_uid' ] ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;\r
739                                         };                                                      \r
740                                 };\r
741                                 tmp = checked[ uid ];\r
742                         };\r
743                         if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
744                 };\r
745                 return res;\r
746         };\r
747         function X_Node_Selector__funcSelectorProp( prop, flag, flags, xnodes ){\r
748                 var res = [],\r
749                         flag_not = flag ? flags.not : !flags.not,\r
750                         i = 0, n = -1, xnode;\r
751                 for( ; xnode = xnodes[ i ]; ++i ){\r
752                         if( xnode[ '_attrs' ] && xnode[ '_attrs' ][ prop ] ^ flag_not ) res[ ++n ] = xnode;\r
753                 };\r
754                 return res;\r
755         };\r
756 \r
757 var X_Node_Selector__filter = {\r
758         'root' : function(){\r
759                 return X_Node_html;\r
760         },\r
761         'target' : {\r
762                 m : function( flags, xnodes ){\r
763                         var res  = [],\r
764                                 hash = location.hash.slice( 1 ),\r
765                                 flag_not = flags.not,\r
766                                 i = 0, n = -1, xnode;\r
767                         for ( ; xnode = xnodes[ i ]; ++i ){\r
768                                 if( ( ( xnode[ '_id' ] || xnode[ '_attrs' ] && xnode[ '_attrs' ][ 'name' ] ) === hash ) ^ flag_not ) res[ ++n ] = xnode;                                                \r
769                         };\r
770                         return res;\r
771                 }\r
772         },\r
773         'first-child' : {\r
774                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( -1, true, flags, xnodes ); }\r
775         },\r
776         'last-child' : {\r
777                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 1, true, flags, xnodes ); }\r
778         },\r
779         'only-child' : {\r
780                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 0, true, flags, xnodes ); }\r
781         },\r
782         'first-of-type' : {\r
783                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( -1, false, flags, xnodes ); }\r
784         },\r
785         'last-of-type' : {\r
786                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 1, false, flags, xnodes ); }\r
787         },\r
788         'only-of-type' : {\r
789                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorChild( 0, false, flags, xnodes ); }\r
790         },\r
791         'nth-child' : {\r
792                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'firstChild', 'next', true, flags, xnodes, a, b ); }\r
793         },\r
794         'nth-last-child' : {\r
795                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'lastChild', 'prev', true, flags, xnodes, a, b ); }\r
796         },\r
797         'nth-of-type' : {\r
798                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'firstChild', 'next', false, flags, xnodes, a, b ); }\r
799         },\r
800         'nth-last-of-type' : {\r
801                 m : function( flags, xnodes, a, b ){ return X_Node_Selector__funcSelectorNth( 'lastChild', 'prev', false, flags, xnodes, a, b ); }\r
802         },\r
803         'empty' : {\r
804                 m : function( flags, xnodes ){\r
805                         var res = [],\r
806                                 flag_not = flags.not,\r
807                                 i = 0, n = -1, xnode, tmp, node;\r
808                         for( ; xnode = xnodes[i]; ++i ){\r
809                                 tmp = true;\r
810                                 for( node = xnode[ 'firstChild' ](); node; node = node[ 'next' ]() ){\r
811                                         if( node[ '_tag' ] || node[ '_text' ] ){\r
812                                                 tmp = false;\r
813                                                 break;\r
814                                         };                              \r
815                                 };\r
816                                 if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
817                         };\r
818                         return res;\r
819                 }\r
820         },\r
821         'link' : {\r
822                 m : function( flags, xnodes ){\r
823                         var links = document.links,\r
824                                 res   = [],\r
825                                 checked, flag_not, i, link, xnode, n;\r
826                         if( !links ) return res;\r
827                         checked = {};\r
828                         flag_not = flags.not;\r
829                         for( i = 0; link = links[ i ]; ++i ){\r
830                                 checked[ ( Node( link ) )[ '_uid' ] ] = true;\r
831                         };\r
832                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
833                                 if( checked[ xnode[ '_uid' ] ] ^ flag_not ) res[ ++n ] = xnode;\r
834                         };\r
835                         return res;\r
836                 }\r
837         },\r
838         'lang' : {\r
839                 m : function( flags, xnodes, arg ){\r
840                         var res = [],\r
841                                 //reg = new RegExp('^' + arg, 'i'),\r
842                                 flag_not = flags.not,\r
843                                 i = 0, n = -1, xnode, tmp, lang;\r
844                         arg = arg.toLowerCase();\r
845                         for( ; tmp = xnode = xnodes[ i ]; ++i ){\r
846                                 while( tmp && !( lang = tmp[ '_attrs' ] && tmp[ '_attrs' ][ 'lang' ] ) ){\r
847                                         tmp = tmp.parent;\r
848                                 };\r
849                                 //tmp = !!(tmp && reg.test(tmp.getAttribute('lang')));\r
850                                 if( ( !!lang && lang.toLowerCase().indexOf( arg ) === 0 ) ^ flag_not ) res[ ++n ] = xnode;\r
851                         };\r
852                         return res;\r
853                 }\r
854         },\r
855         'enabled' : {\r
856                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'disabled', false, flags, xnodes ); }\r
857         },\r
858         'disabled' : {\r
859                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'disabled', true, flags, xnodes ); }\r
860         },\r
861         'checked' : {\r
862                 m : function( flags, xnodes ){ return X_Node_Selector__funcSelectorProp( 'checked', true, flags, xnodes ); }\r
863         },\r
864         'contains' : {\r
865                 m : function( flags, xnodes, arg ){\r
866                         var res = [],\r
867                                 flag_not = flags.not,\r
868                                 i = 0, n = -1, xnode;\r
869                         for( ; xnode = xnodes[ i ]; ++i ){\r
870                                 if ( ( -1 < ( xnode[ 'text' ]() ).indexOf( arg ) ) ^ flag_not ) res[ ++n ] = xnode;                                             \r
871                         };\r
872                         return res;\r
873                 }\r
874         }\r
875 };\r