Merged some recent fixes in from Sizzle.
authorJohn Resig <jeresig@gmail.com>
Mon, 19 Jan 2009 18:09:11 +0000 (18:09 +0000)
committerJohn Resig <jeresig@gmail.com>
Mon, 19 Jan 2009 18:09:11 +0000 (18:09 +0000)
src/selector.js
test/index.html
test/unit/selector.js

index 56c8a2a..682a92e 100644 (file)
@@ -35,34 +35,21 @@ var Sizzle = function(selector, context, results, seed) {
                }
        }
 
-       if ( parts.length > 1 && Expr.match.POS.exec( selector ) ) {
+       if ( parts.length > 1 && origPOS.exec( selector ) ) {
                if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
-                       var later = "", match;
-
-                       // Position selectors must be done after the filter
-                       while ( (match = Expr.match.POS.exec( selector )) ) {
-                               later += match[0];
-                               selector = selector.replace( Expr.match.POS, "" );
-                       }
-
-                       set = Sizzle.filter( later, Sizzle( /\s$/.test(selector) ? selector + "*" : selector, context ) );
+                       set = posProcess( parts[0] + parts[1], context );
                } else {
                        set = Expr.relative[ parts[0] ] ?
                                [ context ] :
                                Sizzle( parts.shift(), context );
 
                        while ( parts.length ) {
-                               var tmpSet = [];
-
                                selector = parts.shift();
+
                                if ( Expr.relative[ selector ] )
                                        selector += parts.shift();
 
-                               for ( var i = 0, l = set.length; i < l; i++ ) {
-                                       Sizzle( selector, set[i], tmpSet );
-                               }
-
-                               set = tmpSet;
+                               set = posProcess( selector, set );
                        }
                }
        } else {
@@ -170,7 +157,7 @@ Sizzle.filter = function(expr, set, inplace, not){
        while ( expr && set.length ) {
                for ( var type in Expr.filter ) {
                        if ( (match = Expr.match[ type ].exec( expr )) != null ) {
-                               var filter = Expr.filter[ type ], goodArray = null, goodPos = 0, found, item;
+                               var filter = Expr.filter[ type ], found, item;
                                anyFound = false;
 
                                if ( curLoop == result ) {
@@ -184,26 +171,13 @@ Sizzle.filter = function(expr, set, inplace, not){
                                                anyFound = found = true;
                                        } else if ( match === true ) {
                                                continue;
-                                       } else if ( match[0] === true ) {
-                                               goodArray = [];
-                                               var last = null, elem;
-                                               for ( var i = 0; (elem = curLoop[i]) !== undefined; i++ ) {
-                                                       if ( elem && last !== elem ) {
-                                                               goodArray.push( elem );
-                                                               last = elem;
-                                                       }
-                                               }
                                        }
                                }
 
                                if ( match ) {
-                                       for ( var i = 0; (item = curLoop[i]) !== undefined; i++ ) {
+                                       for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
                                                if ( item ) {
-                                                       if ( goodArray && item != goodArray[goodPos] ) {
-                                                               goodPos++;
-                                                       }
-       
-                                                       found = filter( item, match, goodPos, goodArray );
+                                                       found = filter( item, match, i, curLoop );
                                                        var pass = not ^ !!found;
 
                                                        if ( inplace && found != null ) {
@@ -358,12 +332,14 @@ var Expr = Sizzle.selectors = {
                CLASS: function(match, curLoop, inplace, result, not){
                        match = " " + match[1].replace(/\\/g, "") + " ";
 
-                       for ( var i = 0; curLoop[i]; i++ ) {
-                               if ( not ^ (" " + curLoop[i].className + " ").indexOf(match) >= 0 ) {
-                                       if ( !inplace )
-                                               result.push( curLoop[i] );
-                               } else if ( inplace ) {
-                                       curLoop[i] = false;
+                       for ( var i = 0; curLoop[i] != null; i++ ) {
+                               if ( curLoop[i] ) {
+                                       if ( not ^ (" " + curLoop[i].className + " ").indexOf(match) >= 0 ) {
+                                               if ( !inplace )
+                                                       result.push( curLoop[i] );
+                                       } else if ( inplace ) {
+                                               curLoop[i] = false;
+                                       }
                                }
                        }
 
@@ -373,8 +349,8 @@ var Expr = Sizzle.selectors = {
                        return match[1].replace(/\\/g, "");
                },
                TAG: function(match, curLoop){
-                       for ( var i = 0; !curLoop[i]; i++ ){}
-                       return isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
+                       for ( var i = 0; curLoop[i] === false; i++ ){}
+                       return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
                },
                CHILD: function(match){
                        if ( match[1] == "nth" ) {
@@ -394,7 +370,7 @@ var Expr = Sizzle.selectors = {
                        return match;
                },
                ATTR: function(match){
-                       var name = match[1];
+                       var name = match[1].replace(/\\/g, "");
                        
                        if ( Expr.attrMap[name] ) {
                                match[1] = Expr.attrMap[name];
@@ -616,6 +592,8 @@ var Expr = Sizzle.selectors = {
        }
 };
 
+var origPOS = Expr.match.POS;
+
 for ( var type in Expr.match ) {
        Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
 }
@@ -750,7 +728,7 @@ if ( document.querySelectorAll ) (function(){
        Sizzle.matches = oldSizzle.matches;
 })();
 
-if ( document.documentElement.getElementsByClassName ) {
+if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) {
        Expr.order.splice(1, 0, "CLASS");
        Expr.find.CLASS = function(match, context) {
                return context.getElementsByClassName(match[1]);
@@ -835,6 +813,26 @@ var isXML = function(elem){
                elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
 };
 
+var posProcess = function(selector, context){
+       var tmpSet = [], later = "", match,
+               root = context.nodeType ? [context] : context;
+
+       // Position selectors must be done after the filter
+       // And so must :not(positional) so we move all PSEUDOs to the end
+       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+               later += match[0];
+               selector = selector.replace( Expr.match.PSEUDO, "" );
+       }
+
+       selector = Expr.relative[selector] ? selector + "*" : selector;
+
+       for ( var i = 0, l = root.length; i < l; i++ ) {
+               Sizzle( selector, root[i], tmpSet );
+       }
+
+       return Sizzle.filter( later, tmpSet );
+}
+
 // EXPOSE
 jQuery.find = Sizzle;
 jQuery.filter = Sizzle.filter;
index cd5a247..e47a852 100644 (file)
@@ -29,7 +29,7 @@
        <h2 id="userAgent"></h2>
        
        <!-- Test HTML -->
-       <div id="nothiddendiv" style="height:1px;background:white;">
+       <div id="nothiddendiv" style="height:1px;background:white;" class="nothiddendiv">
                <div id="nothiddendivchild"></div>
        </div>
        <!-- this iframe is outside the #main so it won't reload constantly wasting time, but it means the tests must be "safe" and clean up after themselves -->
index 638f508..6057236 100644 (file)
@@ -141,7 +141,7 @@ test("multiple", function() {
 });
 
 test("child and adjacent", function() {
-       expect(41);
+       expect(44);
        t( "Child", "p > a", ["simon1","google","groups","mark","yahoo","simon"] );
        t( "Child", "p> a", ["simon1","google","groups","mark","yahoo","simon"] );
        t( "Child", "p >a", ["simon1","google","groups","mark","yahoo","simon"] );
@@ -156,6 +156,11 @@ test("child and adjacent", function() {
        t( "Adjacent", "p + p", ["ap","en","sap"] );
        t( "Comma, Child, and Adjacent", "a + a, code > a", ["groups","anchor1","anchor2"] );
 
+       t( "Verify deep class selector", "div.blah > p > a", [] );
+
+       t( "No element deep selector", "div.foo > span > a", [] );
+       t( "No element not selector", ".container div:not(.excluded) div", [] );
+
        isSet( jQuery("> :first", document.getElementById("nothiddendiv")), q("nothiddendivchild"), "Verify child context positional selctor" );
        isSet( jQuery("> :eq(0)", document.getElementById("nothiddendiv")), q("nothiddendivchild"), "Verify child context positional selctor" );
        isSet( jQuery("> *:first", document.getElementById("nothiddendiv")), q("nothiddendivchild"), "Verify child context positional selctor" );
@@ -239,7 +244,7 @@ test("attributes", function() {
 });
 
 test("pseudo (:) selectors", function() {
-       expect(34);
+       expect(51);
        t( "First Child", "p:first-child", ["firstp","sndp"] );
        t( "Last Child", "p:last-child", ["sap"] );
        t( "Only Child", "a:only-child", ["simon1","anchor1","yahoo","anchor2"] );
@@ -267,6 +272,25 @@ test("pseudo (:) selectors", function() {
        t( "Is A Parent", "p:parent", ["firstp","ap","sndp","en","sap","first"] );
        t( "Is Visible", "#form input:visible", ["text1","text2","radio1","radio2","check1","check2","name"] );
        t( "Is Hidden", "#form input:hidden", ["hidden1","hidden2"] );
+
+       t( "Check position filtering", "div#nothiddendiv:eq(0)", ["nothiddendiv"] );
+       t( "Check position filtering", "div#nothiddendiv:last", ["nothiddendiv"] );
+       t( "Check position filtering", "div#nothiddendiv:not(:gt(0))", ["nothiddendiv"] );
+       t( "Check position filtering", "#foo > :not(:first)", ["en", "sap"] );
+       t( "Check position filtering", "select > :not(:gt(2))", ["option1a", "option1b", "option1c"] );
+       t( "Check position filtering", "select:lt(2) :not(:first)", ["option1b", "option1c", "option1d", "option2a", "option2b", "option2c", "option2d"] );
+       t( "Check position filtering", "div.nothiddendiv:eq(0)", ["nothiddendiv"] );
+       t( "Check position filtering", "div.nothiddendiv:last", ["nothiddendiv"] );
+       t( "Check position filtering", "div.nothiddendiv:not(:gt(0))", ["nothiddendiv"] );
+
+       t( "Check element position", "div div:eq(0)", ["nothiddendivchild"] );
+       t( "Check element position", "div div:eq(5)", ["fadeout"] );
+       t( "Check element position", "div div:eq(27)", ["t2037"] );
+       t( "Check element position", "div div:first", ["nothiddendivchild"] );
+       t( "Check element position", "div > div:first", ["nothiddendivchild"] );
+       t( "Check element position", "#dl div:first div:first", ["foo"] );
+       t( "Check element position", "#dl div:first > div:first", ["foo"] );
+       t( "Check element position", "div#nothiddendiv:first > div:first", ["nothiddendivchild"] );
        
        t( "Form element :input", "#form :input", ["text1", "text2", "radio1", "radio2", "check1", "check2", "hidden1", "hidden2", "name", "button", "area1", "select1", "select2", "select3"] );
        t( "Form element :radio", "#form :radio", ["radio1", "radio2"] );