Minor syntax changes to the winnow function.
[jquery.git] / src / traversing.js
1 // Implement the identical functionality for filter and not
2 var winnow = function( elements, qualifier, keep ) {
3         if ( jQuery.isFunction( qualifier ) ) {
4                 return jQuery.grep(elements, function(elem, i) {
5                         return !!qualifier.call( elem, i ) === keep;
6                 });
7
8         } else if ( qualifier.nodeType ) {
9                 return jQuery.grep(elements, function(elem, i) {
10                         return (elem === qualifier) === keep;
11                 });
12
13         } else if ( typeof qualifier === "string" ) {
14                 var filtered = jQuery.grep(elements, function(elem) {
15                         return elem.nodeType === 1;
16                 });
17
18                 if ( isSimple.test( qualifier ) ) {
19                         return jQuery.filter(qualifier, filtered, !keep);
20                 } else {
21                         qualifier = jQuery.filter( qualifier, elements );
22                 }
23         }
24
25         return jQuery.grep(elements, function(elem, i) {
26                 return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
27         });
28 };
29
30 jQuery.fn.extend({
31         find: function( selector ) {
32                 var ret = this.pushStack( "", "find", selector ), length = 0;
33
34                 for ( var i = 0, l = this.length; i < l; i++ ) {
35                         length = ret.length;
36                         jQuery.find( selector, this[i], ret );
37
38                         if ( i > 0 ) {
39                                 // Make sure that the results are unique
40                                 for ( var n = length; n < ret.length; n++ ) {
41                                         for ( var r = 0; r < length; r++ ) {
42                                                 if ( ret[r] === ret[n] ) {
43                                                         ret.splice(n--, 1);
44                                                         break;
45                                                 }
46                                         }
47                                 }
48                         }
49                 }
50
51                 return ret;
52         },
53
54         not: function( selector ) {
55                 return this.pushStack( winnow(this, selector, false), "not", selector);
56         },
57
58         filter: function( selector ) {
59                 return this.pushStack( winnow(this, selector, true), "filter", selector );
60         },
61
62         closest: function( selectors, context ) {
63                 if ( jQuery.isArray( selectors ) ) {
64                         var ret = [], cur = this[0], match, matches = {}, selector;
65
66                         if ( cur && selectors.length ) {
67                                 for ( var i = 0, l = selectors.length; i < l; i++ ) {
68                                         selector = selectors[i];
69
70                                         if ( !matches[selector] ) {
71                                                 matches[selector] = jQuery.expr.match.POS.test( selector ) ? 
72                                                         jQuery( selector, context || this.context ) :
73                                                         selector;
74                                         }
75                                 }
76
77                                 while ( cur && cur.ownerDocument && cur !== context ) {
78                                         for ( selector in matches ) {
79                                                 match = matches[selector];
80
81                                                 if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
82                                                         ret.push({ selector: selector, elem: cur });
83                                                         delete matches[selector];
84                                                 }
85                                         }
86                                         cur = cur.parentNode;
87                                 }
88                         }
89
90                         return ret;
91                 }
92
93                 var pos = jQuery.expr.match.POS.test( selectors ) ? 
94                         jQuery( selectors, context || this.context ) : null;
95
96                 return this.map(function(i, cur){
97                         while ( cur && cur.ownerDocument && cur !== context ) {
98                                 if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
99                                         return cur;
100                                 }
101                                 cur = cur.parentNode;
102                         }
103                         return null;
104                 });
105         },
106
107         add: function( selector, context ) {
108                 var set = typeof selector === "string" ?
109                                 jQuery( selector, context || this.context ) :
110                                 jQuery.makeArray( selector ),
111                         all = jQuery.merge( this.get(), set );
112
113                 return this.pushStack( set[0] && (set[0].setInterval || set[0].nodeType === 9 || (set[0].parentNode && set[0].parentNode.nodeType !== 11)) ?
114                         jQuery.unique( all ) :
115                         all );
116         },
117
118         eq: function( i ) {
119                 return i === -1 ?
120                         this.slice( i ) :
121                         this.slice( i, +i + 1 );
122         },
123
124         first: function() {
125                 return this.eq( 0 );
126         },
127
128         last: function() {
129                 return this.eq( -1 );
130         },
131
132         slice: function() {
133                 return this.pushStack( Array.prototype.slice.apply( this, arguments ),
134                         "slice", Array.prototype.slice.call(arguments).join(",") );
135         },
136
137         map: function( callback ) {
138                 return this.pushStack( jQuery.map(this, function(elem, i){
139                         return callback.call( elem, i, elem );
140                 }));
141         },
142
143         andSelf: function() {
144                 return this.add( this.prevObject );
145         },
146
147         end: function() {
148                 return this.prevObject || jQuery(null);
149         }
150 });
151
152 jQuery.each({
153         parent: function(elem){return elem.parentNode;},
154         parents: function(elem){return jQuery.dir(elem,"parentNode");},
155         next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
156         prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
157         nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
158         prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
159         siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
160         children: function(elem){return jQuery.sibling(elem.firstChild);},
161         contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
162 }, function(name, fn){
163         jQuery.fn[ name ] = function( selector ) {
164                 var ret = jQuery.map( this, fn );
165
166                 if ( selector && typeof selector === "string" ) {
167                         ret = jQuery.filter( selector, ret );
168                 }
169
170                 ret = this.length > 1 ? jQuery.unique( ret ) : ret;
171
172                 if ( name === "parents" && this.length > 1 ) {
173                         ret = ret.reverse();
174                 }
175
176                 return this.pushStack( ret, name, selector );
177         };
178 });
179
180 jQuery.extend({
181         filter: function( expr, elems, not ) {
182                 if ( not ) {
183                         expr = ":not(" + expr + ")";
184                 }
185
186                 return jQuery.find.matches(expr, elems);
187         },
188         
189         dir: function( elem, dir ) {
190                 var matched = [], cur = elem[dir];
191                 while ( cur && cur.nodeType !== 9 ) {
192                         if ( cur.nodeType === 1 ) {
193                                 matched.push( cur );
194                         }
195                         cur = cur[dir];
196                 }
197                 return matched;
198         },
199
200         nth: function( cur, result, dir, elem ) {
201                 result = result || 1;
202                 var num = 0;
203
204                 for ( ; cur; cur = cur[dir] ) {
205                         if ( cur.nodeType === 1 && ++num === result ) {
206                                 break;
207                         }
208                 }
209
210                 return cur;
211         },
212
213         sibling: function( n, elem ) {
214                 var r = [];
215
216                 for ( ; n; n = n.nextSibling ) {
217                         if ( n.nodeType === 1 && n !== elem ) {
218                                 r.push( n );
219                         }
220                 }
221
222                 return r;
223         }
224 });