]> source.dussan.org Git - jquery.git/commitdiff
Fix #13539: Utilize Sizzle hooks. Close gh-1215.
authorRichard Gibson <richard.gibson@gmail.com>
Wed, 27 Mar 2013 01:20:27 +0000 (21:20 -0400)
committerRichard Gibson <richard.gibson@gmail.com>
Fri, 5 Apr 2013 03:04:05 +0000 (23:04 -0400)
Gruntfile.js
src/attributes.js
src/sizzle
src/sizzle-jquery.js
src/traversing.js
test/unit/css.js
test/unit/selector.js
test/unit/traversing.js

index 0925f67d06d07a8d12ac98498049aa12e48b1cb8..cdddc78c6403cc043038b9584b45bdc5dcdbf8e9 100644 (file)
@@ -41,6 +41,7 @@ module.exports = function( grunt ) {
                                src: [
                                        "src/intro.js",
                                        "src/core.js",
+                                       "src/selector-sizzle.js",
                                        "src/callbacks.js",
                                        "src/deferred.js",
                                        "src/support.js",
@@ -48,7 +49,6 @@ module.exports = function( grunt ) {
                                        "src/queue.js",
                                        "src/attributes.js",
                                        "src/event.js",
-                                       "src/selector-sizzle.js",
                                        "src/traversing.js",
                                        "src/manipulation.js",
                                        { flag: "css", src: "src/css.js" },
index 272b19009e1a782c9e6a05806e5aa28174ee9344..a95e0c70b0762bb63cc6a96a6b9579173215f3c8 100644 (file)
@@ -3,7 +3,6 @@ var nodeHook, boolHook,
        rreturn = /\r/g,
        rfocusable = /^(?:input|select|textarea|button|object)$/i,
        rclickable = /^(?:a|area)$/i,
-       rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
        ruseDefault = /^(?:checked|selected)$/i,
        getSetAttribute = jQuery.support.getSetAttribute,
        getSetInput = jQuery.support.input;
@@ -297,7 +296,7 @@ jQuery.extend({
        },
 
        attr: function( elem, name, value ) {
-               var hooks, notxml, ret,
+               var hooks, ret,
                        nType = elem.nodeType;
 
                // don't get/set attributes on text, comment and attribute nodes
@@ -310,13 +309,12 @@ jQuery.extend({
                        return jQuery.prop( elem, name, value );
                }
 
-               notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
                // All attributes are lowercase
                // Grab necessary hook if one is defined
-               if ( notxml ) {
+               if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
                        name = name.toLowerCase();
-                       hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+                       hooks = jQuery.attrHooks[ name ] ||
+                               ( jQuery.expr.match.boolean.test( name ) ? boolHook : nodeHook );
                }
 
                if ( value !== undefined ) {
@@ -324,7 +322,7 @@ jQuery.extend({
                        if ( value === null ) {
                                jQuery.removeAttr( elem, name );
 
-                       } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+                       } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
                                return ret;
 
                        } else {
@@ -332,16 +330,11 @@ jQuery.extend({
                                return value;
                        }
 
-               } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+               } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
                        return ret;
 
                } else {
-
-                       // In IE9+, Flash objects don't have .getAttribute (#12945)
-                       // Support: IE9+
-                       if ( typeof elem.getAttribute !== core_strundefined ) {
-                               ret =  elem.getAttribute( name );
-                       }
+                       ret = jQuery.find.attr( elem, name );
 
                        // Non-existent attributes return null, we normalize to undefined
                        return ret == null ?
@@ -360,14 +353,15 @@ jQuery.extend({
                                propName = jQuery.propFix[ name ] || name;
 
                                // Boolean attributes get special treatment (#10870)
-                               if ( rboolean.test( name ) ) {
+                               if ( jQuery.expr.match.boolean.test( name ) ) {
                                        // Set corresponding property to false for boolean attributes
-                                       // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
-                                       if ( !getSetAttribute && ruseDefault.test( name ) ) {
+                                       if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+                                               elem[ propName ] = false;
+                                       // Support: IE<9
+                                       // Also clear defaultChecked/defaultSelected (if appropriate)
+                                       } else {
                                                elem[ jQuery.camelCase( "default-" + name ) ] =
                                                        elem[ propName ] = false;
-                                       } else {
-                                               elem[ propName ] = false;
                                        }
 
                                // See #9699 for explanation of this approach (setting first, then removal)
@@ -398,18 +392,8 @@ jQuery.extend({
        },
 
        propFix: {
-               tabindex: "tabIndex",
-               readonly: "readOnly",
                "for": "htmlFor",
-               "class": "className",
-               maxlength: "maxLength",
-               cellspacing: "cellSpacing",
-               cellpadding: "cellPadding",
-               rowspan: "rowSpan",
-               colspan: "colSpan",
-               usemap: "useMap",
-               frameborder: "frameBorder",
-               contenteditable: "contentEditable"
+               "class": "className"
        },
 
        prop: function( elem, name, value ) {
@@ -464,32 +448,8 @@ jQuery.extend({
        }
 });
 
-// Hook for boolean attributes
+// Hooks for boolean attributes
 boolHook = {
-       get: function( elem, name ) {
-               var
-                       // Use .prop to determine if this attribute is understood as boolean
-                       prop = jQuery.prop( elem, name ),
-
-                       // Fetch it accordingly
-                       attr = typeof prop === "boolean" && elem.getAttribute( name ),
-                       detail = typeof prop === "boolean" ?
-
-                               getSetInput && getSetAttribute ?
-                                       attr != null :
-                                       // oldIE fabricates an empty string for missing boolean attributes
-                                       // and conflates checked/selected into attroperties
-                                       ruseDefault.test( name ) ?
-                                               elem[ jQuery.camelCase( "default-" + name ) ] :
-                                               !!attr :
-
-                               // fetch an attribute node for properties not recognized as boolean
-                               elem.getAttributeNode( name );
-
-               return detail && detail.value !== false ?
-                       name.toLowerCase() :
-                       undefined;
-       },
        set: function( elem, value, name ) {
                if ( value === false ) {
                        // Remove boolean attributes when set to false
@@ -506,19 +466,44 @@ boolHook = {
                return name;
        }
 };
+jQuery.each( jQuery.expr.match.boolean.source.match( /\w+/g ), function( i, name ) {
+       var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+
+       jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
+               function( elem, name, isXML ) {
+                       var fn = jQuery.expr.attrHandle[ name ],
+                               ret = isXML ?
+                                       undefined :
+                                       /* jshint eqeqeq: false */
+                                       (jQuery.expr.attrHandle[ name ] = undefined) !=
+                                               getter( elem, name, isXML ) ?
+
+                                               name.toLowerCase() :
+                                               null;
+                       jQuery.expr.attrHandle[ name ] = fn;
+                       return ret;
+               } :
+               function( elem, name, isXML ) {
+                       return isXML ?
+                               undefined :
+                               elem[ jQuery.camelCase( "default-" + name ) ] ?
+                                       name.toLowerCase() :
+                                       null;
+               };
+});
 
-// fix oldIE value attroperty
+// fix oldIE attroperties
 if ( !getSetInput || !getSetAttribute ) {
-       jQuery.attrHooks.value = {
-               get: function( elem, name ) {
-                       var ret = elem.getAttributeNode( name );
-                       return jQuery.nodeName( elem, "input" ) ?
+       jQuery.expr.attrHandle.value = function( elem, name, isXML ) {
+               var ret = elem.getAttributeNode( name );
+               return isXML ? undefined : jQuery.nodeName( elem, "input" ) ?
 
-                               // Ignore the value *property* by using defaultValue
-                               elem.defaultValue :
+                       // Ignore the value *property* by using defaultValue
+                       elem.defaultValue :
 
-                               ret && ret.specified ? ret.value : undefined;
-               },
+                       ret && ret.specified ? ret.value : undefined;
+       };
+       jQuery.attrHooks.value = {
                set: function( elem, value, name ) {
                        if ( jQuery.nodeName( elem, "input" ) ) {
                                // Does not return so that setAttribute is also used
@@ -536,13 +521,7 @@ if ( !getSetAttribute ) {
 
        // Use this for any attribute in IE6/7
        // This fixes almost every IE6/7 issue
-       nodeHook = jQuery.valHooks.button = {
-               get: function( elem, name ) {
-                       var ret = elem.getAttributeNode( name );
-                       return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
-                               ret.value :
-                               undefined;
-               },
+       nodeHook = {
                set: function( elem, value, name ) {
                        // Set the existing or create a new attribute node
                        var ret = elem.getAttributeNode( name );
@@ -560,11 +539,29 @@ if ( !getSetAttribute ) {
                                undefined;
                }
        };
+       jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords =
+               // Some attributes are constructed with empty-string values when not defined
+               function( elem, name, isXML ) {
+                       var ret;
+                       return isXML ?
+                               undefined :
+                               (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
+                                       ret.value :
+                                       null;
+               };
+       jQuery.valHooks.button = {
+               get: function( elem, name ) {
+                       var ret = elem.getAttributeNode( name );
+                       return ret && ret.specified ?
+                               ret.value :
+                               undefined;
+               },
+               set: nodeHook.set
+       };
 
        // Set contenteditable to false on removals(#10429)
        // Setting to empty string throws an error as an invalid value
        jQuery.attrHooks.contenteditable = {
-               get: nodeHook.get,
                set: function( elem, value, name ) {
                        nodeHook.set( elem, value === "" ? false : value, name );
                }
@@ -573,14 +570,14 @@ if ( !getSetAttribute ) {
        // Set width and height to auto instead of 0 on empty string( Bug #8150 )
        // This is for removals
        jQuery.each([ "width", "height" ], function( i, name ) {
-               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+               jQuery.attrHooks[ name ] = {
                        set: function( elem, value ) {
                                if ( value === "" ) {
                                        elem.setAttribute( name, "auto" );
                                        return value;
                                }
                        }
-               });
+               };
        });
 }
 
@@ -588,15 +585,6 @@ if ( !getSetAttribute ) {
 // Some attributes require a special call on IE
 // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
 if ( !jQuery.support.hrefNormalized ) {
-       jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
-               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
-                       get: function( elem ) {
-                               var ret = elem.getAttribute( name, 2 );
-                               return ret == null ? undefined : ret;
-                       }
-               });
-       });
-
        // href/src property should get the full normalized URL (#10299/#12915)
        jQuery.each([ "href", "src" ], function( i, name ) {
                jQuery.propHooks[ name ] = {
@@ -624,7 +612,7 @@ if ( !jQuery.support.style ) {
 // Safari mis-reports the default selected property of an option
 // Accessing the parent's selectedIndex property fixes it
 if ( !jQuery.support.optSelected ) {
-       jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+       jQuery.propHooks.selected = {
                get: function( elem ) {
                        var parent = elem.parentNode;
 
@@ -638,31 +626,43 @@ if ( !jQuery.support.optSelected ) {
                        }
                        return null;
                }
-       });
+       };
 }
 
+jQuery.each([
+       "tabIndex",
+       "readOnly",
+       "maxLength",
+       "cellSpacing",
+       "cellPadding",
+       "rowSpan",
+       "colSpan",
+       "useMap",
+       "frameBorder",
+       "contentEditable"
+], function() {
+       jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
 // IE6/7 call enctype encoding
 if ( !jQuery.support.enctype ) {
        jQuery.propFix.enctype = "encoding";
 }
 
 // Radios and checkboxes getter/setter
-if ( !jQuery.support.checkOn ) {
-       jQuery.each([ "radio", "checkbox" ], function() {
-               jQuery.valHooks[ this ] = {
-                       get: function( elem ) {
-                               // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
-                               return elem.getAttribute("value") === null ? "on" : elem.value;
-                       }
-               };
-       });
-}
 jQuery.each([ "radio", "checkbox" ], function() {
-       jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+       jQuery.valHooks[ this ] = {
                set: function( elem, value ) {
                        if ( jQuery.isArray( value ) ) {
                                return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
                        }
                }
-       });
+       };
+       if ( !jQuery.support.checkOn ) {
+               jQuery.valHooks[ this ].get = function( elem ) {
+                       // Support: Webkit
+                       // "" is returned instead of "on" if a value isn't specified
+                       return elem.getAttribute("value") === null ? "on" : elem.value;
+               };
+       }
 });
index a9ab22e770ab94c4c8b426e2f46f6ab91a19639d..cf19fa8dd6b7fda254bb3434f63d590845eaa7bf 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a9ab22e770ab94c4c8b426e2f46f6ab91a19639d
+Subproject commit cf19fa8dd6b7fda254bb3434f63d590845eaa7bf
index 2370b4493a8972a508e8e56b4f3fd44df75b07fd..fa31965432850658119a98485fb76621afebe3de 100644 (file)
@@ -1,5 +1,3 @@
-// Override sizzle attribute retrieval
-Sizzle.attr = jQuery.attr;
 jQuery.find = Sizzle;
 jQuery.expr = Sizzle.selectors;
 jQuery.expr[":"] = jQuery.expr.pseudos;
index cceb9d5a48c361ede024820c17f956a8d6b3a539..7cc70fb3ad1fff067efee3fcc50bc8e48a5b10c1 100644 (file)
@@ -1,6 +1,4 @@
-var runtil = /Until$/,
-       rparentsprev = /^(?:parents|prev(?:Until|All))/,
-       isSimple = /^.[^:#\[\.,]*$/,
+var isSimple = /^.[^:#\[\.,]*$/,
        rneedsContext = jQuery.expr.match.needsContext,
        // methods guaranteed to produce a unique set when starting from a unique set
        guaranteedUnique = {
@@ -52,11 +50,11 @@ jQuery.fn.extend({
        },
 
        not: function( selector ) {
-               return this.pushStack( winnow(this, selector, false) );
+               return this.pushStack( winnow(this, selector || [], true) );
        },
 
        filter: function( selector ) {
-               return this.pushStack( winnow(this, selector, true) );
+               return this.pushStack( winnow(this, selector || [], false) );
        },
 
        is: function( selector ) {
@@ -188,7 +186,7 @@ jQuery.each({
        jQuery.fn[ name ] = function( until, selector ) {
                var ret = jQuery.map( this, fn, until );
 
-               if ( !runtil.test( name ) ) {
+               if ( name.slice( -5 ) !== "Until" ) {
                        selector = until;
                }
 
@@ -196,10 +194,16 @@ jQuery.each({
                        ret = jQuery.filter( selector, ret );
                }
 
-               ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+               if ( this.length > 1 ) {
+                       // Remove duplicates
+                       if ( !guaranteedUnique[ name ] ) {
+                               ret = jQuery.unique( ret );
+                       }
 
-               if ( this.length > 1 && rparentsprev.test( name ) ) {
-                       ret = ret.reverse();
+                       // Reverse order for parents* and prev*
+                       if ( name.charAt(0) === "p" ) {
+                               ret = ret.reverse();
+                       }
                }
 
                return this.pushStack( ret );
@@ -208,13 +212,17 @@ jQuery.each({
 
 jQuery.extend({
        filter: function( expr, elems, not ) {
+               var elem = elems[ 0 ];
+
                if ( not ) {
                        expr = ":not(" + expr + ")";
                }
 
-               return elems.length === 1 ?
-                       jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
-                       jQuery.find.matches(expr, elems);
+               return elems.length === 1 && elem.nodeType === 1 ?
+                       jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+                       jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+                               return elem.nodeType === 1;
+                       }));
        },
 
        dir: function( elem, dir, until ) {
@@ -244,36 +252,26 @@ jQuery.extend({
 });
 
 // Implement the identical functionality for filter and not
-function winnow( elements, qualifier, keep ) {
-
-       // Can't pass null or undefined to indexOf
-       // Set to 0 to skip string check
-       qualifier = qualifier || 0;
-
+function winnow( elements, qualifier, not ) {
        if ( jQuery.isFunction( qualifier ) ) {
-               return jQuery.grep(elements, function( elem, i ) {
-                       var retVal = !!qualifier.call( elem, i, elem );
-                       return retVal === keep;
+               return jQuery.grep( elements, function( elem, i ) {
+                       return qualifier.call( elem, i, elem ) ? !not : not;
                });
 
        } else if ( qualifier.nodeType ) {
-               return jQuery.grep(elements, function( elem ) {
-                       return ( elem === qualifier ) === keep;
+               return jQuery.grep( elements, function( elem ) {
+                       return ( elem === qualifier ) !== not;
                });
 
        } else if ( typeof qualifier === "string" ) {
-               var filtered = jQuery.grep(elements, function( elem ) {
-                       return elem.nodeType === 1;
-               });
-
                if ( isSimple.test( qualifier ) ) {
-                       return jQuery.filter(qualifier, filtered, !keep);
+                       return jQuery.filter( qualifier, elements, not );
                } else {
-                       qualifier = jQuery.filter( qualifier, filtered );
+                       qualifier = jQuery.filter( qualifier, elements );
                }
        }
 
        return jQuery.grep(elements, function( elem ) {
-               return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+               return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
        });
 }
index 14596c347552578061cb6935ffd79701123f6cad..027cda9e45237c383d262e1f6164ed4d30438a2e 100644 (file)
@@ -904,8 +904,8 @@ test( ":visible/:hidden selectors", function() {
        ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden" );
        jQuery("#nothiddendiv").css({"display": "block"});
        ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible");
-       ok( jQuery(window).is(":visible"), "Calling is(':visible') on window does not throw an error in IE.");
-       ok( jQuery(document).is(":visible"), "Calling is(':visible') on document does not throw an error in IE.");
+       ok( jQuery(window).is(":visible") || true, "Calling is(':visible') on window does not throw an exception (#10267)");
+       ok( jQuery(document).is(":visible") || true, "Calling is(':visible') on document does not throw an exception (#10267)");
 
        ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible");
        jQuery("#nothiddendiv").css("display", "none");
index 0cfba2834161838eacdcd5bf6dfb5f5ff712c2c9..440e7167b58109c212012a4663bc7399d4b2118e 100644 (file)
@@ -31,17 +31,10 @@ test("class - jQuery only", function() {
 });
 
 test("attributes - jQuery only", function() {
-       expect( 6 );
+       expect( 5 );
 
        t( "Find elements with a tabindex attribute", "[tabindex]", ["listWithTabIndex", "foodWithNegativeTabIndex", "linkWithTabIndex", "linkWithNegativeTabIndex", "linkWithNoHrefWithTabIndex", "linkWithNoHrefWithNegativeTabIndex"] );
 
-       // #12523
-       deepEqual(
-               jQuery.find( "[title]", null, null, jQuery("#qunit-fixture a").get().concat( document.createTextNode("") ) ),
-               q("google"),
-               "Text nodes fail attribute tests without exception"
-       );
-
        // #12600
        ok(
                jQuery("<select value='12600'><option value='option' selected='selected'></option><option value=''></option></select>")
index e0dcd11c8e2740f0d7ff846239793a497bd14cbf..c4664dda9d60028b5cea5135261d01e0d91e0829 100644 (file)
@@ -78,10 +78,29 @@ test("is(String|undefined)", function() {
        ok( jQuery("#en").is("[lang=\"de\"] , [lang=\"en\"]"), "Comma-separated; Check for lang attribute: Expect en or de" );
 });
 
-test("is() against window|document (#10178)", function() {
-       expect(2);
-       ok( !jQuery(window).is("a"), "Checking is on a window does not throw an exception" );
-       ok( !jQuery(document).is("a"), "Checking is on a document does not throw an exception" );
+test("is() against non-elements (#10178)", function() {
+       expect(14);
+
+       var label, i, test,
+               collection = jQuery( document ),
+               tests = [ "a", "*" ],
+               nonelements = {
+                       text: document.createTextNode(""),
+                       comment: document.createComment(""),
+                       document: document,
+                       window: window,
+                       array: [],
+                       "plain object": {},
+                       "function": function() {}
+               };
+
+       for ( label in nonelements ) {
+               collection[ 0 ] = nonelements[ label ];
+               for ( i = 0; i < tests.length; i++ ) {
+                       test = tests[ i ];
+                       ok( !collection.is( test ), label + " does not match \"" + test + "\"" );
+               }
+       }
 });
 
 test("is(jQuery)", function() {
@@ -711,3 +730,15 @@ test("index(no arg) #10977", function() {
        strictEqual ( jQuery( "#indextest li.zero" ).first().index() , 0, "No Argument Index Check" );
        $list.remove();
 });
+
+test("traversing non-elements with attribute filters (#12523)", function() {
+       expect(5);
+
+       var nonnodes = jQuery("#nonnodes").contents();
+
+       equal( nonnodes.filter("[id]").length, 1, ".filter" );
+       equal( nonnodes.find("[id]").length, 0, ".find" );
+       strictEqual( nonnodes.is("[id]"), true, ".is" );
+       deepEqual( nonnodes.closest("[id='nonnodes']").get(), q("nonnodes"), ".closest" );
+       deepEqual( nonnodes.parents("[id='nonnodes']").get(), q("nonnodes"), ".parents" );
+});