OSDN Git Service

6af421b350063ec3c4f9856d60453d0716e5b763
[pettanr/clientJs.git] / 0.6.x / js / dom / 18_XDomQuery.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 X.Dom.Query = {\r
11         _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         _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         },\r
42         _SELECTOR : {\r
43                 ''    : 0, // none\r
44                 tag   : 1,\r
45                 '#'   : 2,\r
46                 '.'   : 3,\r
47                 ':'   : 4,\r
48                 '['   : 5,\r
49                 not   : 6,\r
50                 scope : 7,\r
51                 root  : 8,\r
52                 link  : 9\r
53         },\r
54         _OPERATORS : { '==' : 1, '!=': 2, '~=': 3, '^=': 4, '$=': 5, '*=': 6, '|=': 7 },\r
55         _ALPHABET  : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789\\',\r
56         _NUMBER    : '+-0123456789'\r
57 };\r
58                         \r
59 /*\r
60  * セレクタ文字の解析\r
61  * 結合子 + 単体セレクタ( タグ,*,#,.,[],: )\r
62  * ' ' 子孫セレクタ, '>' 直下,'+','~' の場合、tagName|* を返す\r
63  * return [ pointer, [ selector, ... ] ] error: return pointer\r
64  */\r
65 X.Dom.Query._parse = function( query, last ){\r
66         var COMBINATOR = X.Dom.Query._COMBINATOR,\r
67                 SELECTOR   = X.Dom.Query._SELECTOR,\r
68                 OPERATORS  = X.Dom.Query._OPERATORS,\r
69                 ALPHABET   = X.Dom.Query._ALPHABET,\r
70                 NUMBER     = X.Dom.Query._NUMBER,\r
71                 result     = [],\r
72                 i          = -1,\r
73                 l          = query.length,\r
74                 phase      = 0x0,\r
75                 combinator = 0,\r
76                 selector   = 0,\r
77                 chr, chrCode, nameChr, name1st,\r
78                 tmp, escape, quot, start,\r
79                 name, key, value, operator, a, b, not;\r
80         query += ' ';\r
81         while( i < l ){\r
82                 chr     = query.charAt( ++i );\r
83                 chrCode = ALPHABET.indexOf( chr );\r
84                 nameChr = chrCode !== -1;\r
85                 name1st = nameChr && chrCode < 52;\r
86                 switch( phase ){\r
87                         case 0x0 :\r
88                                 name1st ? // tagName\r
89                                         ( ( selector = 1 ) && ( phase = 0x2 ) && ( start = i ) ) :\r
90                                 !not && ( tmp = COMBINATOR[ chr ] ) ? (\r
91                                         ( 1 < tmp && 1 < combinator ) ?\r
92                                                 ( phase = 0xf ) :\r
93                                                 ( phase = tmp === 5 ? 0xe : 0x0 ) & ( ( 1 < tmp || combinator < 1 ) && ( combinator = tmp ) ) & ( tmp === 5 && ++i ) ) : // ' ' でない結合子の上書きはエラー\r
94                                 ( tmp = SELECTOR[ chr ] ) ? // [\r
95                                         ( selector = tmp ) && ( phase = selector === 5 ? 0x4 : 0x1 ) : // 7:[, 0<:\r
96                                 chr === '*' ?\r
97                                         ( ( selector = 1 ) && ( name = chr ) && ( phase = 0xe ) && ++i ) :\r
98                                         chr !== ' ' && ( phase = 0xf );\r
99                                 //console.log( '0x0: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase + ' tmp:' + tmp + ' comb:' + combinator );\r
100                                 break;\r
101                         case 0x1 :\r
102                                 name1st ?\r
103                                         ( ( start = i ) && ( phase = 0x2 ) ) :\r
104                                         chr !== ' ' && ( phase = 0xf );\r
105                                 break;\r
106                         case 0x2 :\r
107                                 !nameChr && !( escape && ( selector === 2 || selector === 3 ) && ( chr === ':' || chr === '.' ) ) ? // id or class の場合 : . を直前にエスケープした場合に限り使える\r
108                                         ( name = query.substring( start, i ) ) && ( phase = selector === 4 && name !== 'not' && chr === '(' ? 0x8 : 0xe ) :\r
109                                 SELECTOR[ chr ] < 4 && ( phase = 0xe );\r
110                                 break;\r
111                         \r
112                         case 0x3 : //:nth-\r
113                                 break;\r
114                         \r
115                         case 0x4 : // start attr filter\r
116                                 name1st ?\r
117                                         ( ( phase = 0x5 ) && ( start = i ) ) :\r
118                                         ( phase = 0xf );\r
119                                 break;\r
120                         case 0x5 : // attr filter key\r
121                                 chr === '=' ?\r
122                                         ( ( operator = 1 ) && ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = i + 1 ) ) :\r
123                                 chr === ']' ?\r
124                                         ( !( operator = 0 ) && ( phase = 0xe ) && ( key || ( key = query.substring( start, i ) ) ) && ++i ) :\r
125                                 chr === ' ' ?\r
126                                         ( key || ( key = query.substring( start, i ) ) ) :\r
127                                 ( operator = OPERATORS[ query.substr( i, 2 ) ] ) ?\r
128                                         ( ( phase = 0x6 ) && ( key || ( key = query.substring( start, i ) ) ) && ( start = ++i ) ) :\r
129                                         !nameChr && ( phase = 0xf );\r
130                                 //console.log( name1st + ' ' + chrCode + chr + phase );\r
131                                 break;\r
132                         case 0x6 :\r
133                                 ( chr === '"' || chr === "'" ) && !escape && !quot ?\r
134                                         ( quot = chr ) && ( start = i + 1 ) && ( phase = 0x7 ) :\r
135                                         chr !== ' ' && ( start = i ) && ( phase = 0x7 );\r
136                                 break;\r
137                                 \r
138                         case 0x7 : // attr filter value\r
139                                 chr === quot ?\r
140                                         !escape && !value && ( value = query.substring( start, i ) ) :\r
141                                 chr === ']' ?\r
142                                         ( ( value || ( value = query.substring( start, i ) ) ) && ( phase = 0xe ) && ++i ) :    \r
143                                 chr === ' ' && !quot && !value && ( value = query.substring( start, i ) );                      \r
144                                         //( chr === '"' || chr === "'" ) && !quot && ( quot = chr ) && ( start = i + 1 );\r
145                                 break;\r
146                                 \r
147                         case 0x8 : // 4, 2n, even, odd, -n+4,\r
148                                 NUMBER.indexOf( chr ) !== -1 ?\r
149                                         ( start = i ) && ( phase = 0x9 ) :\r
150                                 chr === 'n' ?\r
151                                         ( phase = 0xa ) && ( a = 1 ) && ( start = i + 1 ) :\r
152                                 query.substr( i, 4 ) === 'even' ?\r
153                                         ( ( a = 2 ) && !( b = 0 ) && ( i += 3 ) ) :\r
154                                 query.substr( i, 3 ) === 'odd' ?\r
155                                         ( ( a = 2 ) && ( b = 1 ) && ( i += 2 ) ) :                                      \r
156                                 chr === ')' && ( phase = a ? 0xe : 0xf ) && ++i;\r
157                                 //console.log( '0x8: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase );\r
158                                 break;\r
159                         case 0x9 :\r
160                                 tmp = query.substring( start, i );\r
161                                 chr === 'n' ?\r
162                                         ( phase = 0xa ) && ( start = i + 1 ) && ( a = tmp === '+' ? 1 : tmp === '-' ? -1 : parseFloat( tmp ) ) :\r
163                                 chr === ')' && ( phase = 0xe ) && ( b = parseFloat( tmp ) ) && ++i && ( a = 0 );\r
164                                 //console.log( '0x9: ' + name1st + ' ' + chrCode + ' ' + chr + ' ' + phase );\r
165                                 break;\r
166                         case 0xa :\r
167                                 chr === ')' && ( phase = 0xe ) && ++i && ( b = parseFloat( query.substring( start, i ) ) || 0 );\r
168                                 //console.log( '0xa: ' + start + ' ' + i );\r
169                                 break;\r
170                         default :\r
171                 };\r
172                 //alert( chr + ' ' + phase + ' ' + selector + ' ' + name + ' ' + name1st )\r
173                 if( phase === 0xe ){\r
174                         if( selector === 4 ){// :not\r
175                                 if( name === 'not' ){\r
176                                         if( not ) return i; // error\r
177                                         not      = true;\r
178                                         selector = 0;\r
179                                         phase    = 0x0;\r
180                                         name     = null;\r
181                                         continue;\r
182                                 } else {\r
183                                         if( a !== a || b !== b ) return i;\r
184                                         result = [ not ? 0 : combinator, selector, name, a, b ];\r
185                                         break;  \r
186                                 };\r
187                                 // lang result = [ not ? 0 : combinator, selector, name, lan ];\r
188                                 // contains result = [ not ? 0 : combinator, selector, name, text ];\r
189                         };\r
190                         result =\r
191                                 combinator === 5 ?\r
192                                         5 :\r
193                                 selector === 5 ?\r
194                                         [ not ? 0 : combinator, selector, key, operator, value ] :\r
195                                         [ not ? 0 : combinator, selector, name.split( '\\' ).join( '' ) ];\r
196                         break;\r
197                 } else\r
198                 if( phase === 0xf ) return i;\r
199 \r
200                 escape = chr === '\\' && !escape;\r
201         };\r
202         //if( phase !== 0xe ) return i;\r
203         if( not && ( tmp = query.substr( i ).indexOf( ')' ) ) === -1 ) return i;\r
204         return not ? [ i + tmp + 1, [ combinator, 6, result ] ] : [ i, result ];\r
205 };\r
206 \r
207         // セレクター\r
208         X.Dom.find = Node.prototype.find = function( queryString ){\r
209                 var scope   = [ this.cnstructor === Node ? this : Node.root ],\r
210                         noLower   = 'title id name class for href src',\r
211                         ARY_PUSH  = Array.prototype.push,\r
212                         ret       = [], // 結果要素\r
213                         root      = X.Dom.Node.getRoot( scope[ 0 ] ),\r
214                         isXML     = !!X.Dom.Node.isXmlDocument( root ),\r
215                         isMulti   = 1 < scope.length,// 要素をマージする必要がある\r
216                         parents   = scope, // 探索元の親要素\r
217                         l, i, n, parsed,\r
218                         xnodes, // 一時保存用\r
219                         merge, // 要素がコメントノードで汚染されている場合使う\r
220                         combinator, selector, name, tagName, \r
221                         isAll, isStart = true, isNot,\r
222                         uid, tmp, xnode, filter, key, op, val, toLower, useName,\r
223             links, className, attr, flag;\r
224 \r
225                 // 文字列以外は空で返す\r
226                 if( typeof queryString !== 'string' ) return ret;\r
227                 \r
228                 \r
229                 \r
230                 xnodes = [];\r
231                 \r
232                 // 以下、パースと探索\r
233                 for( ; queryString.length; ){\r
234                         console.log( 'queryString[' + queryString + ']' + queryString.length );\r
235                         \r
236                         // 初期化処理\r
237                         if( !parsed ){\r
238                                 parsed = X.Dom.Query._parse( queryString );\r
239                                 \r
240                                 if( typeof parsed === 'number' ){\r
241                                         // error\r
242                                         return [];\r
243                                 };\r
244                                 \r
245                                 queryString = queryString.substr( parsed[ 0 ] );\r
246                                 parsed      = parsed[ 1 ];\r
247                                 \r
248                                 console.log( 'X.Dom.Query._parse ' + parsed );\r
249                                 \r
250                                 if( parsed === 5 ){\r
251                                         isMulti = true;\r
252                                         parents = scope;\r
253                                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
254                                         parsed  = null;\r
255                                         xnodes  = null;\r
256                                         isStart = true;\r
257                                         continue;\r
258                                 };\r
259                         };\r
260                         \r
261 \r
262                         \r
263                         combinator  = parsed[ 0 ];\r
264                         selector    = parsed[ 1 ];\r
265                         name        = parsed[ 2 ];\r
266                         tagName     = selector === 1 ? ( isXML ? name : name.toUpperCase() ) : '*';\r
267                         isAll       = tagName === '*';\r
268         \r
269                         if( !isStart ){\r
270                                 if( !xnodes.length ){\r
271                                         parsed = null;\r
272                                         continue;                                       \r
273                                 } else\r
274                                 if( combinator !== 0 ){\r
275                                         parents = xnodes;\r
276                                         xnodes  = [];\r
277                                         console.log( 'cobinator !== 0 ' + parents.length + ' : ' + xnodes.length );\r
278                                 };\r
279                         };\r
280                         \r
281                         i = 0;\r
282                         l = parents.length;\r
283                         n = -1; \r
284                         isMulti = isMulti || 1 < l;\r
285                         \r
286                         //console.log( 'combinator ' + combinator );\r
287         \r
288                         switch( combinator ){\r
289                                 // > TagName|*\r
290                                 case 2 :\r
291                                         for( ; i < l; ++i ){\r
292                                                 for( xnode = parents[ i ].firstChild(); xnode; xnode = xnode.nextNode() ){\r
293                                                         if( xnode._xnodeType === 1 && ( isAll || tagName === xnode._tag ) ) xnodes[ ++n ] = xnode;\r
294                                                 };                              \r
295                                         };\r
296                                         break;\r
297                                 // + TagName|*\r
298                                 case 3 :\r
299                                         for( ; i < l; ++i ){\r
300                                                 for( xnode = parents[ i ].nextNode(); xnode; xnode = xnode.nextNode() ){\r
301                                                         if( xnode._xnodeType === 1 ){\r
302                                                                 if( isAll || tagName === xnode._tag ) xnodes[ ++n ] = xnode;\r
303                                                                 break;\r
304                                                         };                                                                      \r
305                                                 };                                                              \r
306                                         };\r
307                                         break;\r
308                                 // ~ TagName|*\r
309                                 case 4 :\r
310                                         merge  = {};\r
311                                         for( ; i < l; ++i ){\r
312                                                 for( xnode = parents[ i ].nextNode(); xnode; xnode = xnode.nextNode() ){\r
313                                                         if( xnode._xnodeType === 1 && ( isAll || tagName === xnode._tag ) ){\r
314                                                                 uid = xnode._uid;\r
315                                                                 if( merge[ uid ] ){\r
316                                                                         break;\r
317                                                                 } else {\r
318                                                                         merge[ uid ] = true;\r
319                                                                         xnodes[ ++n ] = xnode;\r
320                                                                 };\r
321                                                         };                                                                      \r
322                                                 };                                                              \r
323                                         };\r
324                                         break;\r
325                                         \r
326                                 default :\r
327                                         if( combinator === 1 || isStart ){\r
328                                                 console.log( l + ' > ' + xnodes.length + ' tag:' + tagName );\r
329                                                 for( ; i < l; ++i ){\r
330                                                         xnode = parents[ i ];\r
331                                                         xnode._xnodes && xnode._xnodes.length && X.Dom.Query._fetchElements( xnodes, xnode, isAll ? null : tagName );\r
332                                                 };\r
333                                                 console.log( l + ' >> ' + xnodes.length + ' tag:' + tagName );\r
334                                         };\r
335                         };\r
336                         \r
337                         isStart = false;\r
338                         \r
339                         //alert( 'pre-selector:' + ( xnodes && xnodes.length ) )\r
340                         \r
341                         switch( selector ){\r
342                                 // #, ID\r
343                                 case 2 :\r
344                                         filter = [ 'id', 1, name ]; break;\r
345                                 // ., class\r
346                                 case 3 :\r
347                                         filter = [ 'class', 3 /*'~='*/, name ]; break;\r
348                                 // :, 擬似クラス\r
349                                 case 4 :\r
350                                         if( !( filter = X.Dom.Query._filter[ name ] ) ){\r
351                                                 return [];\r
352                                         };\r
353                                         break;\r
354                                 // [] 属性\r
355                                 case 5 :\r
356                                         filter = [ name, parsed[ 3 ], parsed[ 4 ] ]; break;\r
357                                 // :not\r
358                                 case 6 :\r
359                                         isNot  = true;\r
360                                         parsed = parsed[ 2 ];\r
361                                         continue;\r
362                                 // scope\r
363                                 case 7 :\r
364                                         xnodes = scope; break;\r
365                                 // root\r
366                                 case 8 :\r
367                                         xnodes = [ Node._html ]; break;\r
368                                 // link\r
369                                 case 9 :\r
370                                         if( links = root._rawNode.links ){\r
371                                                 for( xnodes = [], i = links.length; i; ){\r
372                                                         xnodes[ --i ] = new Node( links[ i ] );\r
373                                                 };\r
374                                         };\r
375                         };\r
376                         \r
377                         if( filter && xnodes.length ){\r
378                                 // filter.mが関数の場合\r
379                                 if( filter.m ){\r
380                                         xnodes = filter.m(\r
381                                                 {\r
382                                                         not : isNot,\r
383                                                         xml : isXML\r
384                                                 },\r
385                                                 xnodes,\r
386                                                 parsed[ 3 ], parsed[ 4 ]\r
387                                         );\r
388                                 } else\r
389                                 // filterが関数の場合\r
390                                 if( typeof filter === 'function' ){\r
391                                         tmp = [];\r
392                                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
393                                                 if( ( !!filter( xnode ) ) ^ isNot ) tmp[ ++n ] = xnode; \r
394                                         };\r
395                                         xnodes = tmp;\r
396                                 } else {\r
397                                 // 属性セレクター                        \r
398                                         tmp = [];\r
399                                         key = filter[ 0 ];\r
400                                         op  = filter[ 1 ];\r
401                                         val = filter[ 2 ]; \r
402                                         // [class~='val']\r
403                                         if( !isXML && key === 'class' && op === 3 ){\r
404                                                 val = ' ' + val + ' ';\r
405                                                 for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
406                                                         className = xnode._className;\r
407                                                         if( !!( className && ( ' ' + className + ' ' ).indexOf( val ) > -1 ) ^ isNot ) tmp[ ++n ] = xnode;\r
408                                                 };\r
409                                         } else {\r
410                                         // 通常\r
411                                                 // 諦めて、funcAttrを呼ぶ\r
412                                                 // flag_call  = ($.browser.safari && key === 'selected');\r
413                                                 // getAttributeを使わない\r
414                                                 useName = X.UA.IE && key !== 'href' && key !== 'src';\r
415                                                 toLower = !!val && !isXML && noLower.indexOf( key ) === -1; //!noLower.test(key);\r
416                                                 if( toLower ) val = val.toLowerCase();\r
417                                                 if( op === 3 ) val = ' ' + val + ' ';\r
418 \r
419                                                 for( i = 0, n = -1, l = xnodes.length; i < l; ++i ){\r
420                                                         xnode = xnodes[ i ];\r
421                                                         attr =\r
422                                                                 key === 'id' ? xnode._id :\r
423                                                                 key === 'class' ? xnode._className :\r
424                                                                 xnode._attrs && xnode._attrs[ key ];\r
425                                                                 //flag_call ?\r
426                                                                 //      funcAttr( elem, key ) :\r
427                                                                 //useName ?\r
428                                                                 //      elem[ X.Dom.Attr.renameForDOM[ key ] || key ] :\r
429                                                                 //      elem.getAttribute( key, 2 );\r
430                                                         flag = attr != null && ( !useName || attr !== '' );\r
431                                                         if( flag && op ){\r
432                                                                 if( toLower ) attr = attr.toLowerCase();\r
433                                                                 \r
434                                                                 switch( op ){\r
435                                                                         case 1: // =\r
436                                                                                 flag = attr === val;\r
437                                                                                 break;\r
438                                                                         case 2: // !=\r
439                                                                                 flag = attr !== val;\r
440                                                                                 break;\r
441                                                                         case 3: // ~=\r
442                                                                                 flag = ( ' ' + attr + ' ' ).indexOf( val ) !== -1;\r
443                                                                                 break;\r
444                                                                         case 4: // ^=\r
445                                                                                 flag = attr.indexOf( val ) === 0;\r
446                                                                                 break;\r
447                                                                         case 5: // $=\r
448                                                                                 flag = attr.lastIndexOf( val ) + val.length === attr.length;\r
449                                                                                 break;\r
450                                                                         case 6: // *=\r
451                                                                                 flag = attr.indexOf( val ) !== -1;\r
452                                                                                 break;\r
453                                                                         case 7: // |=\r
454                                                                                 flag = attr === val || attr.substring( 0, val.length + 1 ) === val + '-';\r
455                                                                                 break;\r
456                                                                 };\r
457                                                         };\r
458                                                         if( !!flag ^ isNot ) tmp[ ++n ] = xnode;\r
459                                                 };\r
460                                         };\r
461                                         xnodes = tmp;\r
462                                 };\r
463                         };\r
464                         filter  = null;\r
465                         isNot   = false;\r
466                         parsed  = null;\r
467                         \r
468                         console.log( '//end :' + ( xnodes && xnodes.length ) );\r
469                 };\r
470                 console.log( 'multi:' + ( xnodes && xnodes.length ) )\r
471                 \r
472                 if( isMulti ){\r
473                         xnodes && xnodes.length && ARY_PUSH.apply( ret, xnodes );\r
474                         //for( i = 0, l = xnodes.length, n = ret.length - 1; i < l; ++i ){\r
475                         //      ret[ ++n ] = xnodes[ i ];\r
476                         //};\r
477                         xnodes = [];\r
478                         merge  = {};\r
479                         for( i = 0, n = -1, l = ret.length; i < l; ++i ){\r
480                                 //alert( 'multi:' + i )\r
481                                 xnode = ret[ i ];\r
482                                 uid   = xnode._uid;\r
483                                 if( !merge[ uid ] ){\r
484                                         merge[ uid ] = true;\r
485                                         xnodes[ ++n ] = xnode;\r
486                                 };\r
487                         };\r
488                 };\r
489 \r
490                 return xnodes.length === 1 ? xnodes[ 0 ] : new X.Dom.NodeList( xnodes );\r
491         };\r
492         \r
493         X.Dom.Query._fetchElements = function( list, parent, tag ){\r
494                 var xnodes = parent._xnodes,\r
495                         l      = xnodes.length,\r
496                         i      = 0,\r
497                         child;\r
498                 for( ; i < l; ++i ){\r
499                         child = xnodes[ i ];\r
500                         if( child._xnodeType === 1 ){\r
501                                 ( !tag || child._tag === tag ) && ( list[ list.length ] = child );\r
502                                 //console.log( parent._tag + ' > ' + child._tag + ' == ' + tag+ ' l:' + list.length );\r
503                                 child._xnodes && child._xnodes.length && X.Dom.Query._fetchElements( list, child, tag );\r
504                         };\r
505                 };\r
506         };\r
507 \r
508         X.Dom.Query._funcSelectorChild = function( type, flag_all, flags, xnodes ){\r
509                 var res      = [],\r
510                         flag_not = flags.not,\r
511                         i = 0, n = -1, xnode, node,\r
512                         tagName, tmp;\r
513                 for( ; xnode = xnodes[ i ]; ++i ){\r
514                         tagName = flag_all || xnode._tag;\r
515                         tmp     = null;\r
516                         if( /* tmp === null && */ type <= 0 ){\r
517                                 for( node = xnode.prevNode(); node; node = node.prevNode() ){\r
518                                         if( node._xnodeType === 1 && ( flag_all || tagName === node._tag ) ){\r
519                                                 tmp = false;\r
520                                                 break;\r
521                                         };\r
522                                 };\r
523                         };\r
524                         if( tmp === null && 0 <= type ){\r
525                                 for( node = xnode.nextNode(); node; node = node.nextNode() ){\r
526                                         if( node._xnodeType === 1 && ( flag_all || tagName === node._tag ) ){\r
527                                                 tmp = false;\r
528                                                 break;\r
529                                         };              \r
530                                 };                                              \r
531                         };\r
532                         if( tmp === null ) tmp = true;\r
533                         if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
534                 };\r
535                 return res;\r
536         };\r
537         X.Dom.Query._funcSelectorNth = function( pointer, sibling, flag_all, flags, xnodes, a, b ){\r
538                 var _data    = funcData,\r
539                         res      = [],\r
540                         checked  = {},\r
541                         flag_not = flags.not,\r
542                         i = 0, n = -1, elem, uid,\r
543                         c, xnode, tmp, node, tagName;\r
544                 for( ; xnode = xnodes[ i ]; ++i ){\r
545                         uid = xnode._uid;\r
546                         tmp = checked[ uid ];\r
547                         if( tmp === void 0 ){\r
548                                 for( c = 0, node = xnode.parent[ pointer ](), tagName = flag_all || xnode._tag; node; node = node[ sibling ]() ){\r
549                                         if( node._xnodeType === 1 && ( flag_all || tagName === node._tag ) ){\r
550                                                 ++c;\r
551                                                 checked[ node._uid ] = a === 0 ? c === b : (c - b) % a === 0 && (c - b) / a >= 0;\r
552                                         };                                                      \r
553                                 };\r
554                                 tmp = checked[ uid ];\r
555                         };\r
556                         if( tmp ^ flag_not ) res[ ++n ] = elem;\r
557                 };\r
558                 return res;\r
559         };\r
560         X.Dom.Query._funcSelectorProp = function( prop, flag, flags, xnodes ){\r
561                 var res = [],\r
562                         flag_not = flag ? flags.not : !flags.not,\r
563                         i = 0, n = -1, xnode;\r
564                 for( ; xnode = xnodes[ i ]; ++i ){\r
565                         if( xnode._attrs && xnode._attrs[ prop ] ^ flag_not ) res[ ++n ] = xnode;\r
566                 };\r
567                 return res;\r
568         };\r
569 \r
570 X.Dom.Query._filter = {\r
571         root : function( elem ){\r
572                 return elem === ( elem.ownerDocument || elem.document ).documentElement;\r
573         },\r
574         target : {\r
575                 m : function( flags, xnodes ){\r
576                         var res  = [],\r
577                                 hash = location.hash.slice( 1 ),\r
578                                 flag_not = flags.not,\r
579                                 i = 0, n = -1, xnode;\r
580                         for ( ; xnode = xnodes[ i ]; ++i ){\r
581                                 if( ( ( xnode._id || xnode._attrs && xnode._attrs.name ) === hash ) ^ flag_not ) res[ ++n ] = xnode;                                            \r
582                         };\r
583                         return res;\r
584                 }\r
585         },\r
586         'first-child' : {\r
587                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( -1, true, flags, xnodes ); }\r
588         },\r
589         'last-child' : {\r
590                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( 1, true, flags, xnodes ); }\r
591         },\r
592         'only-child' : {\r
593                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( 0, true, flags, xnodes ); }\r
594         },\r
595         'first-of-type' : {\r
596                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( -1, false, flags, xnodes ); }\r
597         },\r
598         'last-of-type' : {\r
599                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( 1, false, flags, xnodes ); }\r
600         },\r
601         'only-of-type' : {\r
602                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorChild( 0, false, flags, xnodes ); }\r
603         },\r
604         'nth-child' : {\r
605                 m : function( flags, xnodes, a, b ){ return X.Dom.Query._funcSelectorNth( 'firstChild', 'nextNode', true, flags, xnodes, a, b ); }\r
606         },\r
607         'nth-last-child' : {\r
608                 m : function( flags, xnodes, a, b ){ return X.Dom.Query._funcSelectorNth( 'lastChild', 'prevNode', true, flags, xnodes, a, b ); }\r
609         },\r
610         'nth-of-type' : {\r
611                 m : function( flags, xnodes, a, b ){ return X.Dom.Query._funcSelectorNth( 'firstChild', 'nextNode', false, flags, xnodes, a, b ); }\r
612         },\r
613         'nth-last-of-type' : {\r
614                 m : function( flags, xnodes, a, b ){ return X.Dom.Query._funcSelectorNth( 'lastChild', 'prevNode', false, flags, xnodes, a, b ); }\r
615         },\r
616         empty : {\r
617                 m : function( flags, xnodes ){\r
618                         var res = [],\r
619                                 flag_not = flags.not,\r
620                                 i = 0, n = -1, xnode, tmp, node;\r
621                         for( ; xnode = xnodes[i]; ++i ){\r
622                                 tmp = true;\r
623                                 for( node = xnode.firstChild(); node; node = node.nextSibling() ){\r
624                                         if( node._xnodeType === 1 || ( node._xnodeType === 3 && node._text ) ){\r
625                                                 tmp = false;\r
626                                                 break;\r
627                                         };                              \r
628                                 };\r
629                                 if( tmp ^ flag_not ) res[ ++n ] = xnode;\r
630                         };\r
631                         return res;\r
632                 }\r
633         },\r
634         link : {\r
635                 m : function( flags, xnodes ){\r
636                         var links = ( xnodes[ 0 ].ownerDocument || xnodes[ 0 ].document ).links,\r
637                                 _data = funcData,\r
638                                 res   = [],\r
639                                 checked, flag_not, i, link, xnode, n;\r
640                         if( !links ) return res;\r
641                         checked = {};\r
642                         flag_not = flags.not;\r
643                         for( i = 0; link = links[ i ]; ++i ){\r
644                                 checked[ ( new Node( link ) )._uid ] = true;\r
645                         };\r
646                         for( i = 0, n = -1; xnode = xnodes[ i ]; ++i ){\r
647                                 if( checked[ xnode._uid ] ^ flag_not ) res[ ++n ] = xnode;\r
648                         };\r
649                         return res;\r
650                 }\r
651         },\r
652         lang : {\r
653                 m : function( flags, xnodes, arg ){\r
654                         var res = [],\r
655                                 //reg = new RegExp('^' + arg, 'i'),\r
656                                 flag_not = flags.not,\r
657                                 i = 0, n = -1, xnode, tmp, lang;\r
658                         arg = arg.toLowerCase();\r
659                         for( ; tmp = xnode = xnodes[ i ]; ++i ){\r
660                                 while( tmp && !( lang = tmp._attrs && tmp._attrs[ 'lang' ] ) ){\r
661                                         tmp = tmp.parent;\r
662                                 };\r
663                                 //tmp = !!(tmp && reg.test(tmp.getAttribute('lang')));\r
664                                 if( ( !!( tmp && lang && lang.toLowerCase().indexOf( arg ) === 0 ) ) ^ flag_not ) res[ ++n ] = xnode;\r
665                         };\r
666                         return res;\r
667                 }\r
668         },\r
669         enabled : {\r
670                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorProp( 'disabled', false, flags, xnodes ); }\r
671         },\r
672         disabled : {\r
673                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorProp( 'disabled', true, flags, xnodes ); }\r
674         },\r
675         checked : {\r
676                 m : function( flags, xnodes ){ return X.Dom.Query._funcSelectorProp( 'checked', true, flags, xnodes ); }\r
677         },\r
678         contains : {\r
679                 m : function( flags, xnodes, arg ){\r
680                         var res = [],\r
681                                 textContent = $.browser.msie || $.browser.opera ? 'innerText' : 'textContent',\r
682                                 flag_not = flags.not,\r
683                                 i = 0, n = -1, elem;\r
684                         for( ; elem = xnodes[ i ]; ++i ){\r
685                                 if ( ( -1 < ( elem[ textContent ] || '' ).indexOf( arg ) ) ^ flag_not ) res[ ++n ] = elem;                                              \r
686                         };\r
687                         return res;\r
688                 }\r
689         }\r
690 };\r
691 \r