Skip to content

Commit

Permalink
Landing Ben Alman's patch to add nextUntil, prevUntil, and parentsUnt…
Browse files Browse the repository at this point in the history
…il. Also adds some tests for prevAll and nextAll, and fixes an test edge case in parents.
  • Loading branch information
cowboy authored and jeresig committed Dec 4, 2009
1 parent bbd933c commit 2b481b9
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 11 deletions.
5 changes: 3 additions & 2 deletions src/core.js
Expand Up @@ -484,13 +484,14 @@ jQuery.extend({
return ret;
},

map: function( elems, callback ) {
// arg is for internal usage only
map: function( elems, callback, arg ) {
var ret = [], value;

// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
value = callback( elems[ i ], i );
value = callback( elems[ i ], i, arg );

if ( value != null ) {
ret[ ret.length ] = value;
Expand Down
30 changes: 22 additions & 8 deletions src/traversing.js
@@ -1,3 +1,10 @@
var runtil = /Until$/,
rparentsprev = /^(?:parents|prevUntil|prevAll)/,
// Note: This RegExp should be improved, or likely pulled from Sizzle
rmultiselector = /,/,
slice = Array.prototype.slice,
join = Array.prototype.join;

// Implement the identical functionality for filter and not
var winnow = function( elements, qualifier, keep ) {
if ( jQuery.isFunction( qualifier ) ) {
Expand Down Expand Up @@ -130,8 +137,8 @@ jQuery.fn.extend({
},

slice: function() {
return this.pushStack( Array.prototype.slice.apply( this, arguments ),
"slice", Array.prototype.slice.call(arguments).join(",") );
return this.pushStack( slice.apply( this, arguments ),
"slice", join.call(arguments, ",") );
},

map: function( callback ) {
Expand All @@ -152,28 +159,35 @@ jQuery.fn.extend({
jQuery.each({
parent: function(elem){return elem.parentNode;},
parents: function(elem){return jQuery.dir(elem,"parentNode");},
parentsUntil: function(elem,i,until){return jQuery.dir(elem,"parentNode",until);},
next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
nextUntil: function(elem,i,until){return jQuery.dir(elem,"nextSibling",until);},
prevUntil: function(elem,i,until){return jQuery.dir(elem,"previousSibling",until);},
siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
children: function(elem){return jQuery.sibling(elem.firstChild);},
contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
}, function(name, fn){
jQuery.fn[ name ] = function( selector ) {
var ret = jQuery.map( this, fn );
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );

if ( !runtil.test( name ) ) {
selector = until;
}

if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}

ret = this.length > 1 ? jQuery.unique( ret ) : ret;

if ( name === "parents" && this.length > 1 ) {
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
ret = ret.reverse();
}

return this.pushStack( ret, name, selector );
return this.pushStack( ret, name, join.call(arguments, ",") );
};
});

Expand All @@ -186,9 +200,9 @@ jQuery.extend({
return jQuery.find.matches(expr, elems);
},

dir: function( elem, dir ) {
dir: function( elem, dir, until ) {
var matched = [], cur = elem[dir];
while ( cur && cur.nodeType !== 9 ) {
while ( cur && cur.nodeType !== 9 && (until === undefined || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
Expand Down
74 changes: 73 additions & 1 deletion test/unit/traversing.js
Expand Up @@ -193,10 +193,26 @@ test("parents([String])", function() {
equals( jQuery("#groups").parents()[0].id, "ap", "Simple parents check" );
equals( jQuery("#groups").parents("p")[0].id, "ap", "Filtered parents check" );
equals( jQuery("#groups").parents("div")[0].id, "main", "Filtered parents check2" );
same( jQuery("#groups").parents("p, div").get(), q("main", "ap"), "Check for multiple filters" );
same( jQuery("#groups").parents("p, div").get(), q("ap", "main"), "Check for multiple filters" );
same( jQuery("#en, #sndp").parents().get(), q("foo", "main", "dl", "body", "html"), "Check for unique results from parents" );
});

test("parentsUntil([String])", function() {
expect(9);

var parents = jQuery("#groups").parents();

same( jQuery("#groups").parentsUntil().get(), parents.get(), "parentsUntil with no selector (nextAll)" );
same( jQuery("#groups").parentsUntil(".foo").get(), parents.get(), "parentsUntil with invalid selector (nextAll)" );
same( jQuery("#groups").parentsUntil("#html").get(), parents.not(':last').get(), "Simple parentsUntil check" );
equals( jQuery("#groups").parentsUntil("#ap").length, 0, "Simple parentsUntil check" );
same( jQuery("#groups").parentsUntil("#html, #body").get(), parents.slice( 0, 3 ).get(), "Less simple parentsUntil check" );
same( jQuery("#groups").parentsUntil("#html", "div").get(), jQuery("#main").get(), "Filtered parentsUntil check" );
same( jQuery("#groups").parentsUntil("#html", "p,div,dl").get(), parents.slice( 0, 3 ).get(), "Multiple-filtered parentsUntil check" );
equals( jQuery("#groups").parentsUntil("#html", "span").length, 0, "Filtered parentsUntil check, no match" );
same( jQuery("#groups, #ap").parentsUntil("#html", "p,div,dl").get(), parents.slice( 0, 3 ).get(), "Multi-source, multiple-filtered parentsUntil check" );
});

test("next([String])", function() {
expect(4);
equals( jQuery("#ap").next()[0].id, "foo", "Simple next check" );
Expand All @@ -213,6 +229,62 @@ test("prev([String])", function() {
equals( jQuery("#foo").prev("p, div")[0].id, "ap", "Multiple filters" );
});

test("nextAll([String])", function() {
expect(4);

var elems = jQuery('#form').children();

same( jQuery("#label-for").nextAll().get(), elems.not(':first').get(), "Simple nextAll check" );
same( jQuery("#label-for").nextAll('input').get(), elems.not(':first').filter('input').get(), "Filtered nextAll check" );
same( jQuery("#label-for").nextAll('input,select').get(), elems.not(':first').filter('input,select').get(), "Multiple-filtered nextAll check" );
same( jQuery("#label-for, #hidden1").nextAll('input,select').get(), elems.not(':first').filter('input,select').get(), "Multi-source, multiple-filtered nextAll check" );
});

test("prevAll([String])", function() {
expect(4);

var elems = jQuery( jQuery('#form').children().slice(0, 12).get().reverse() );

same( jQuery("#area1").prevAll().get(), elems.get(), "Simple prevAll check" );
same( jQuery("#area1").prevAll('input').get(), elems.filter('input').get(), "Filtered prevAll check" );
same( jQuery("#area1").prevAll('input,select').get(), elems.filter('input,select').get(), "Multiple-filtered prevAll check" );
same( jQuery("#area1, #hidden1").prevAll('input,select').get(), elems.filter('input,select').get(), "Multi-source, multiple-filtered prevAll check" );
});

test("nextUntil([String])", function() {
expect(10);

var elems = jQuery('#form').children().slice( 2, 12 );

same( jQuery("#text1").nextUntil().get(), jQuery("#text1").nextAll().get(), "nextUntil with no selector (nextAll)" );
same( jQuery("#text1").nextUntil(".foo").get(), jQuery("#text1").nextAll().get(), "nextUntil with invalid selector (nextAll)" );
same( jQuery("#text1").nextUntil("#area1").get(), elems.get(), "Simple nextUntil check" );
equals( jQuery("#text1").nextUntil("#text2").length, 0, "Simple nextUntil check" );
same( jQuery("#text1").nextUntil("#area1, #radio1").get(), jQuery("#text1").next().get(), "Less simple nextUntil check" );
same( jQuery("#text1").nextUntil("#area1", "input").get(), elems.not("button").get(), "Filtered nextUntil check" );
same( jQuery("#text1").nextUntil("#area1", "button").get(), elems.not("input").get(), "Filtered nextUntil check" );
same( jQuery("#text1").nextUntil("#area1", "button,input").get(), elems.get(), "Multiple-filtered nextUntil check" );
equals( jQuery("#text1").nextUntil("#area1", "div").length, 0, "Filtered nextUntil check, no match" );
same( jQuery("#text1, #hidden1").nextUntil("#area1", "button,input").get(), elems.get(), "Multi-source, multiple-filtered nextUntil check" );
});

test("prevUntil([String])", function() {
expect(10);

var elems = jQuery("#area1").prevAll();

same( jQuery("#area1").prevUntil().get(), elems.get(), "prevUntil with no selector (prevAll)" );
same( jQuery("#area1").prevUntil(".foo").get(), elems.get(), "prevUntil with invalid selector (prevAll)" );
same( jQuery("#area1").prevUntil("label").get(), elems.not(':last').get(), "Simple prevUntil check" );
equals( jQuery("#area1").prevUntil("#button").length, 0, "Simple prevUntil check" );
same( jQuery("#area1").prevUntil("label, #search").get(), jQuery("#area1").prev().get(), "Less simple prevUntil check" );
same( jQuery("#area1").prevUntil("label", "input").get(), elems.not(':last').not("button").get(), "Filtered prevUntil check" );
same( jQuery("#area1").prevUntil("label", "button").get(), elems.not(':last').not("input").get(), "Filtered prevUntil check" );
same( jQuery("#area1").prevUntil("label", "button,input").get(), elems.not(':last').get(), "Multiple-filtered prevUntil check" );
equals( jQuery("#area1").prevUntil("label", "div").length, 0, "Filtered prevUntil check, no match" );
same( jQuery("#area1, #hidden1").prevUntil("label", "button,input").get(), elems.not(':last').get(), "Multi-source, multiple-filtered prevUntil check" );
});

test("slice()", function() {
expect(7);

Expand Down

0 comments on commit 2b481b9

Please sign in to comment.