Fix #13797: .is with single-node context
[jquery.git] / src / traversing.js
blob5e9fe19438b0e4a6ab55ab740dba1b1d35f75bcd
1 var isSimple = /^.[^:#\[\.,]*$/,
2         rneedsContext = jQuery.expr.match.needsContext,
3         // methods guaranteed to produce a unique set when starting from a unique set
4         guaranteedUnique = {
5                 children: true,
6                 contents: true,
7                 next: true,
8                 prev: true
9         };
11 jQuery.fn.extend({
12         find: function( selector ) {
13                 var i,
14                         ret = [],
15                         self = this,
16                         len = self.length;
18                 if ( typeof selector !== "string" ) {
19                         return this.pushStack( jQuery( selector ).filter(function() {
20                                 for ( i = 0; i < len; i++ ) {
21                                         if ( jQuery.contains( self[ i ], this ) ) {
22                                                 return true;
23                                         }
24                                 }
25                         }) );
26                 }
28                 for ( i = 0; i < len; i++ ) {
29                         jQuery.find( selector, self[ i ], ret );
30                 }
32                 // Needed because $( selector, context ) becomes $( context ).find( selector )
33                 ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
34                 ret.selector = this.selector ? this.selector + " " + selector : selector;
35                 return ret;
36         },
38         has: function( target ) {
39                 var targets = jQuery( target, this ),
40                         l = targets.length;
42                 return this.filter(function() {
43                         var i = 0;
44                         for ( ; i < l; i++ ) {
45                                 if ( jQuery.contains( this, targets[i] ) ) {
46                                         return true;
47                                 }
48                         }
49                 });
50         },
52         not: function( selector ) {
53                 return this.pushStack( winnow(this, selector || [], true) );
54         },
56         filter: function( selector ) {
57                 return this.pushStack( winnow(this, selector || [], false) );
58         },
60         is: function( selector ) {
61                 return !!winnow(
62                         this,
64                         // If this is a positional/relative selector, check membership in the returned set
65                         // so $("p:first").is("p:last") won't return true for a doc with two "p".
66                         typeof selector === "string" && rneedsContext.test( selector ) ?
67                                 jQuery( selector ) :
68                                 selector || [],
69                         false
70                 ).length;
71         },
73         closest: function( selectors, context ) {
74                 var cur,
75                         i = 0,
76                         l = this.length,
77                         matched = [],
78                         pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ?
79                                 jQuery( selectors, context || this.context ) :
80                                 0;
82                 for ( ; i < l; i++ ) {
83                         for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
84                                 // Always skip document fragments
85                                 if ( cur.nodeType < 11 && (pos ?
86                                         pos.index(cur) > -1 :
88                                         // Don't pass non-elements to Sizzle
89                                         cur.nodeType === 1 &&
90                                                 jQuery.find.matchesSelector(cur, selectors)) ) {
92                                         cur = matched.push( cur );
93                                         break;
94                                 }
95                         }
96                 }
98                 return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
99         },
101         // Determine the position of an element within
102         // the matched set of elements
103         index: function( elem ) {
105                 // No argument, return index in parent
106                 if ( !elem ) {
107                         return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
108                 }
110                 // index in selector
111                 if ( typeof elem === "string" ) {
112                         return core_indexOf.call( jQuery( elem ), this[ 0 ] );
113                 }
115                 // Locate the position of the desired element
116                 return core_indexOf.call( this,
118                         // If it receives a jQuery object, the first element is used
119                         elem.jquery ? elem[ 0 ] : elem
120                 );
121         },
123         add: function( selector, context ) {
124                 var set = typeof selector === "string" ?
125                                 jQuery( selector, context ) :
126                                 jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
127                         all = jQuery.merge( this.get(), set );
129                 return this.pushStack( jQuery.unique(all) );
130         },
132         addBack: function( selector ) {
133                 return this.add( selector == null ?
134                         this.prevObject : this.prevObject.filter(selector)
135                 );
136         }
139 function sibling( cur, dir ) {
140         while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
142         return cur;
145 jQuery.each({
146         parent: function( elem ) {
147                 var parent = elem.parentNode;
148                 return parent && parent.nodeType !== 11 ? parent : null;
149         },
150         parents: function( elem ) {
151                 return jQuery.dir( elem, "parentNode" );
152         },
153         parentsUntil: function( elem, i, until ) {
154                 return jQuery.dir( elem, "parentNode", until );
155         },
156         next: function( elem ) {
157                 return sibling( elem, "nextSibling" );
158         },
159         prev: function( elem ) {
160                 return sibling( elem, "previousSibling" );
161         },
162         nextAll: function( elem ) {
163                 return jQuery.dir( elem, "nextSibling" );
164         },
165         prevAll: function( elem ) {
166                 return jQuery.dir( elem, "previousSibling" );
167         },
168         nextUntil: function( elem, i, until ) {
169                 return jQuery.dir( elem, "nextSibling", until );
170         },
171         prevUntil: function( elem, i, until ) {
172                 return jQuery.dir( elem, "previousSibling", until );
173         },
174         siblings: function( elem ) {
175                 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
176         },
177         children: function( elem ) {
178                 return jQuery.sibling( elem.firstChild );
179         },
180         contents: function( elem ) {
181                 return jQuery.nodeName( elem, "iframe" ) ?
182                         elem.contentDocument || elem.contentWindow.document :
183                         jQuery.merge( [], elem.childNodes );
184         }
185 }, function( name, fn ) {
186         jQuery.fn[ name ] = function( until, selector ) {
187                 var matched = jQuery.map( this, fn, until );
189                 if ( name.slice( -5 ) !== "Until" ) {
190                         selector = until;
191                 }
193                 if ( selector && typeof selector === "string" ) {
194                         matched = jQuery.filter( selector, matched );
195                 }
197                 if ( this.length > 1 ) {
198                         // Remove duplicates
199                         if ( !guaranteedUnique[ name ] ) {
200                                 jQuery.unique( matched );
201                         }
203                         // Reverse order for parents* and prev*
204                         if ( name[ 0 ] === "p" ) {
205                                 matched.reverse();
206                         }
207                 }
209                 return this.pushStack( matched );
210         };
213 jQuery.extend({
214         filter: function( expr, elems, not ) {
215                 var elem = elems[ 0 ];
217                 if ( not ) {
218                         expr = ":not(" + expr + ")";
219                 }
221                 return elems.length === 1 && elem.nodeType === 1 ?
222                         jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
223                         jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
224                                 return elem.nodeType === 1;
225                         }));
226         },
228         dir: function( elem, dir, until ) {
229                 var matched = [],
230                         truncate = until !== undefined;
232                 while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
233                         if ( elem.nodeType === 1 ) {
234                                 if ( truncate && jQuery( elem ).is( until ) ) {
235                                         break;
236                                 }
237                                 matched.push( elem );
238                         }
239                 }
240                 return matched;
241         },
243         sibling: function( n, elem ) {
244                 var matched = [];
246                 for ( ; n; n = n.nextSibling ) {
247                         if ( n.nodeType === 1 && n !== elem ) {
248                                 matched.push( n );
249                         }
250                 }
252                 return matched;
253         }
256 // Implement the identical functionality for filter and not
257 function winnow( elements, qualifier, not ) {
258         if ( jQuery.isFunction( qualifier ) ) {
259                 return jQuery.grep( elements, function( elem, i ) {
260                         /* jshint -W018 */
261                         return !!qualifier.call( elem, i, elem ) !== not;
262                 });
264         }
266         if ( qualifier.nodeType ) {
267                 return jQuery.grep( elements, function( elem ) {
268                         return ( elem === qualifier ) !== not;
269                 });
271         }
273         if ( typeof qualifier === "string" ) {
274                 if ( isSimple.test( qualifier ) ) {
275                         return jQuery.filter( qualifier, elements, not );
276                 }
278                 qualifier = jQuery.filter( qualifier, elements );
279         }
281         return jQuery.grep( elements, function( elem ) {
282                 return ( core_indexOf.call( qualifier, elem ) >= 0 ) !== not;
283         });