* * Positional selectors (:first; :eq(n); :odd; etc.)
* * Type selectors (:input; :checkbox; :button; etc.)
* * State-based selectors (:animated; :visible; :hidden; etc.)
- * * :has(selector)
- * * :not(complex selector)
+ * * :has(selector) in browsers without native support
+ * * :not(complex selector) in IE
* * custom selectors via jQuery extensions
- * * Leading combinators (e.g., $collection.find("> *"))
* * Reliable functionality on XML fragments
- * * Requiring all parts of a selector to match elements under context
- * (e.g., $div.find("div > *") now matches children of $div)
* * Matching against non-elements
* * Reliable sorting of disconnected nodes
* * querySelectorAll bug fixes (e.g., unreliable :focus on WebKit)
import jQuery from "./core.js";
import document from "./var/document.js";
-import documentElement from "./var/documentElement.js";
import whitespace from "./var/whitespace.js";
// The following utils are attached directly to the jQuery object.
import "./selector/escapeSelector.js";
import "./selector/uniqueSort.js";
+import isIE from "./var/isIE.js";
+import booleans from "./selector/var/booleans.js";
+import rleadingCombinator from "./selector/var/rleadingCombinator.js";
+import rdescend from "./selector/var/rdescend.js";
+import rsibling from "./selector/var/rsibling.js";
+import matches from "./selector/var/matches.js";
+import testContext from "./selector/testContext.js";
+import filterMatchExpr from "./selector/filterMatchExpr.js";
+import preFilter from "./selector/preFilter.js";
+import tokenize from "./selector/tokenize.js";
+import toSelector from "./selector/toSelector.js";
-// Support: IE 9 - 11+
-// IE requires a prefix.
-var matches = documentElement.matches || documentElement.msMatchesSelector;
+var matchExpr = jQuery.extend( {
+ bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
+ needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
+}, filterMatchExpr );
jQuery.extend( {
find: function( selector, context, results, seed ) {
- var elem, nodeType,
+ var elem, nid, groups, newSelector,
+ newContext = context && context.ownerDocument,
+
+ // nodeType defaults to 9, since context defaults to document
+ nodeType = context ? context.nodeType : 9,
i = 0;
results = results || [];
}
// Early return if context is not an element, document or document fragment
- if ( ( nodeType = context.nodeType ) !== 1 && nodeType !== 9 && nodeType !== 11 ) {
+ if ( nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
return [];
}
}
}
} else {
- jQuery.merge( results, context.querySelectorAll( selector ) );
+
+ newSelector = selector;
+ newContext = context;
+
+ // qSA considers elements outside a scoping root when evaluating child or
+ // descendant combinators, which is not what we want.
+ // In such cases, we work around the behavior by prefixing every selector in the
+ // list with an ID selector referencing the scope context.
+ // The technique has to be used as well when a leading combinator is used
+ // as such selectors are not recognized by querySelectorAll.
+ // Thanks to Andrew Dupont for this technique.
+ if ( nodeType === 1 &&
+ ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
+
+ // Expand context for sibling selectors
+ newContext = rsibling.test( selector ) &&
+ testContext( context.parentNode ) ||
+ context;
+
+ // Outside of IE, if we're not changing the context we can
+ // use :scope instead of an ID.
+ if ( newContext !== context || isIE ) {
+
+ // Capture the context ID, setting it first if necessary
+ if ( ( nid = context.getAttribute( "id" ) ) ) {
+ nid = jQuery.escapeSelector( nid );
+ } else {
+ context.setAttribute( "id", ( nid = jQuery.expando ) );
+ }
+ }
+
+ // Prefix every selector in the list
+ groups = tokenize( selector );
+ i = groups.length;
+ while ( i-- ) {
+ groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
+ toSelector( groups[ i ] );
+ }
+ newSelector = groups.join( "," );
+ }
+
+ try {
+ jQuery.merge( results, newContext.querySelectorAll( newSelector ) );
+ } finally {
+ if ( nid === jQuery.expando ) {
+ context.removeAttribute( "id" );
+ }
+ }
}
return results;
},
expr: {
- attrHandle: {},
- match: {
- bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" +
- "|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ),
- needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
- }
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ match: matchExpr,
+ preFilter: preFilter
}
} );
import jQuery from "./core.js";
import nodeName from "./core/nodeName.js";
import document from "./var/document.js";
-import documentElement from "./var/documentElement.js";
import indexOf from "./var/indexOf.js";
import pop from "./var/pop.js";
import push from "./var/push.js";
import rbuggyQSA from "./selector/rbuggyQSA.js";
import rtrim from "./var/rtrim.js";
import isIE from "./var/isIE.js";
+import identifier from "./selector/var/identifier.js";
+import booleans from "./selector/var/booleans.js";
+import rleadingCombinator from "./selector/var/rleadingCombinator.js";
+import rdescend from "./selector/var/rdescend.js";
+import rsibling from "./selector/var/rsibling.js";
+import matches from "./selector/var/matches.js";
+import createCache from "./selector/createCache.js";
+import testContext from "./selector/testContext.js";
+import filterMatchExpr from "./selector/filterMatchExpr.js";
+import preFilter from "./selector/preFilter.js";
+import selectorError from "./selector/selectorError.js";
+import unescapeSelector from "./selector/unescapeSelector.js";
+import tokenize from "./selector/tokenize.js";
+import toSelector from "./selector/toSelector.js";
import support from "./selector/support.js";
// The following utils are attached directly to the jQuery object.
import "./selector/escapeSelector.js";
import "./selector/uniqueSort.js";
-var preferredDoc = document,
- matches = documentElement.matches || documentElement.msMatchesSelector;
+var preferredDoc = document;
( function() {
var i,
- Expr,
outermostContext,
// Local document vars
documentIsHTML,
// Instance-specific data
- expando = jQuery.expando,
dirruns = 0,
done = 0,
classCache = createCache(),
- tokenCache = createCache(),
compilerCache = createCache(),
nonnativeSelectorCache = createCache(),
- booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
- "loop|multiple|open|readonly|required|scoped",
-
// Regular expressions
- // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
- identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
- "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
-
- // Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors
- attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
-
- // Operator (capture 2)
- "*([*^$|!~]?=)" + whitespace +
-
- // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
- "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
- whitespace + "*\\]",
-
- pseudos = ":(" + identifier + ")(?:\\((" +
-
- // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
- // 1. quoted (capture 3; capture 4 or capture 5)
- "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
-
- // 2. simple (capture 6)
- "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
-
- // 3. anything else (capture 2)
- ".*" +
- ")\\)|)",
-
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rwhitespace = new RegExp( whitespace + "+", "g" ),
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
- whitespace + "*" ),
- rdescend = new RegExp( whitespace + "|>" ),
-
- rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ),
- matchExpr = {
- ID: new RegExp( "^#(" + identifier + ")" ),
- CLASS: new RegExp( "^\\.(" + identifier + ")" ),
- TAG: new RegExp( "^(" + identifier + "|[*])" ),
- ATTR: new RegExp( "^" + attributes ),
- PSEUDO: new RegExp( "^" + pseudos ),
- CHILD: new RegExp(
- "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
- whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
- whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ matchExpr = jQuery.extend( {
bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
needsContext: new RegExp( "^" + whitespace +
"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
- },
+ }, filterMatchExpr ),
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i,
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
- rsibling = /[+~]/,
-
- // CSS escapes
- // https://www.w3.org/TR/CSS21/syndata.html#escaped-characters
- runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
- "?|\\\\([^\\r\\n\\f])", "g" ),
- funescape = function( escape, nonHex ) {
- var high = "0x" + escape.slice( 1 ) - 0x10000;
-
- if ( nonHex ) {
-
- // Strip the backslash prefix from a non-hex escape sequence
- return nonHex;
- }
-
- // Replace a hexadecimal escape sequence with the encoded Unicode code point
- // Support: IE <=11+
- // For values outside the Basic Multilingual Plane (BMP), manually construct a
- // surrogate pair
- return high < 0 ?
- String.fromCharCode( high + 0x10000 ) :
- String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
- },
-
// Used for iframes; see `setDocument`.
// Support: IE 9 - 11+
// Removing the function wrapper causes a "Permission Denied"
{ dir: "parentNode", next: "legend" }
);
-function selectorError( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-}
-
function find( selector, context, results, seed ) {
var m, i, elem, nid, match, groups, newSelector,
newContext = context && context.ownerDocument,
// as such selectors are not recognized by querySelectorAll.
// Thanks to Andrew Dupont for this technique.
if ( nodeType === 1 &&
- ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
+ ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
// Expand context for sibling selectors
- newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
+ newContext = rsibling.test( selector ) &&
+ testContext( context.parentNode ) ||
context;
// Outside of IE, if we're not changing the context we can
if ( ( nid = context.getAttribute( "id" ) ) ) {
nid = jQuery.escapeSelector( nid );
} else {
- context.setAttribute( "id", ( nid = expando ) );
+ context.setAttribute( "id", ( nid = jQuery.expando ) );
}
}
} catch ( qsaError ) {
nonnativeSelectorCache( selector, true );
} finally {
- if ( nid === expando ) {
+ if ( nid === jQuery.expando ) {
context.removeAttribute( "id" );
}
}
return select( selector.replace( rtrim, "$1" ), context, results, seed );
}
-/**
- * Create key-value caches of limited size
- * @returns {function(string, object)} Returns the Object data after storing it on itself with
- * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- * deleting the oldest entry
- */
-function createCache() {
- var keys = [];
-
- function cache( key, value ) {
-
- // Use (key + " ") to avoid collision with native prototype properties
- // (see https://github.com/jquery/sizzle/issues/157)
- if ( keys.push( key + " " ) > Expr.cacheLength ) {
-
- // Only keep the most recent entries
- delete cache[ keys.shift() ];
- }
- return ( cache[ key + " " ] = value );
- }
- return cache;
-}
-
/**
* Mark a function for special use by jQuery selector module
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
- fn[ expando ] = true;
+ fn[ jQuery.expando ] = true;
return fn;
}
} );
}
-/**
- * Checks a node for validity as a jQuery selector context
- * @param {Element|Object=} context
- * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
- */
-function testContext( context ) {
- return context && typeof context.getElementsByTagName !== "undefined" && context;
-}
-
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [node] An element or document object to use to set the document
return find( expr, document, null, [ elem ] ).length > 0;
};
-Expr = jQuery.expr = {
+jQuery.expr = {
// Can be adjusted by the user
cacheLength: 50,
"~": { dir: "previousSibling" }
},
- preFilter: {
- ATTR: function( match ) {
- match[ 1 ] = match[ 1 ].replace( runescape, funescape );
-
- // Move the given value to match[3] whether quoted or unquoted
- match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" )
- .replace( runescape, funescape );
-
- if ( match[ 2 ] === "~=" ) {
- match[ 3 ] = " " + match[ 3 ] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- CHILD: function( match ) {
-
- /* matches from matchExpr["CHILD"]
- 1 type (only|nth|...)
- 2 what (child|of-type)
- 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
- 4 xn-component of xn+y argument ([+-]?\d*n|)
- 5 sign of xn-component
- 6 x of xn-component
- 7 sign of y-component
- 8 y of y-component
- */
- match[ 1 ] = match[ 1 ].toLowerCase();
-
- if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
-
- // nth-* requires argument
- if ( !match[ 3 ] ) {
- selectorError( match[ 0 ] );
- }
-
- // numeric x and y parameters for Expr.filter.CHILD
- // remember that false/true cast respectively to 0/1
- match[ 4 ] = +( match[ 4 ] ?
- match[ 5 ] + ( match[ 6 ] || 1 ) :
- 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
- );
- match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
-
- // other types prohibit arguments
- } else if ( match[ 3 ] ) {
- selectorError( match[ 0 ] );
- }
-
- return match;
- },
-
- PSEUDO: function( match ) {
- var excess,
- unquoted = !match[ 6 ] && match[ 2 ];
-
- if ( matchExpr.CHILD.test( match[ 0 ] ) ) {
- return null;
- }
-
- // Accept quoted arguments as-is
- if ( match[ 3 ] ) {
- match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
-
- // Strip excess characters from unquoted arguments
- } else if ( unquoted && rpseudo.test( unquoted ) &&
-
- // Get excess from tokenize (recursively)
- ( excess = tokenize( unquoted, true ) ) &&
-
- // advance to the next closing parenthesis
- ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
-
- // excess is a negative index
- match[ 0 ] = match[ 0 ].slice( 0, excess );
- match[ 2 ] = unquoted.slice( 0, excess );
- }
-
- // Return only captures needed by the pseudo filter method (type and argument)
- return match.slice( 0, 3 );
- }
- },
+ preFilter: preFilter,
filter: {
ID: function( id ) {
- var attrId = id.replace( runescape, funescape );
+ var attrId = unescapeSelector( id );
return function( elem ) {
return elem.getAttribute( "id" ) === attrId;
};
},
TAG: function( nodeNameSelector ) {
- var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ var expectedNodeName = unescapeSelector( nodeNameSelector ).toLowerCase();
return nodeNameSelector === "*" ?
function() {
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
- outerCache = parent[ expando ] || ( parent[ expando ] = {} );
+ outerCache = parent[ jQuery.expando ] ||
+ ( parent[ jQuery.expando ] = {} );
cache = outerCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex && cache[ 2 ];
// Use previously-cached element index if available
if ( useCache ) {
- outerCache = elem[ expando ] || ( elem[ expando ] = {} );
+ outerCache = elem[ jQuery.expando ] ||
+ ( elem[ jQuery.expando ] = {} );
cache = outerCache[ type ] || [];
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
diff = nodeIndex;
// Cache the index of each encountered element
if ( useCache ) {
- outerCache = node[ expando ] ||
- ( node[ expando ] = {} );
+ outerCache = node[ jQuery.expando ] ||
+ ( node[ jQuery.expando ] = {} );
outerCache[ type ] = [ dirruns, diff ];
}
// https://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
- var fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ var fn = jQuery.expr.pseudos[ pseudo ] ||
+ jQuery.expr.setFilters[ pseudo.toLowerCase() ] ||
selectorError( "unsupported pseudo: " + pseudo );
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as jQuery does
- if ( fn[ expando ] ) {
+ if ( fn[ jQuery.expando ] ) {
return fn( argument );
}
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) );
- return matcher[ expando ] ?
+ return matcher[ jQuery.expando ] ?
markFunction( function( seed, matches, _context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
} ),
contains: markFunction( function( text ) {
- text = text.replace( runescape, funescape );
+ text = unescapeSelector( text );
return function( elem ) {
return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
};
if ( !ridentifier.test( lang || "" ) ) {
selectorError( "unsupported lang: " + lang );
}
- lang = lang.replace( runescape, funescape ).toLowerCase();
+ lang = unescapeSelector( lang ).toLowerCase();
return function( elem ) {
var elemLang;
do {
},
parent: function( elem ) {
- return !Expr.pseudos.empty( elem );
+ return !jQuery.expr.pseudos.empty( elem );
},
// Element/input types
}
};
-Expr.pseudos.nth = Expr.pseudos.eq;
+jQuery.expr.pseudos.nth = jQuery.expr.pseudos.eq;
// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
- Expr.pseudos[ i ] = createInputPseudo( i );
+ jQuery.expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
- Expr.pseudos[ i ] = createButtonPseudo( i );
+ jQuery.expr.pseudos[ i ] = createButtonPseudo( i );
}
// Easy API for creating new setFilters
function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-function tokenize( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ selector + " " ];
-
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
-
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
-
- while ( soFar ) {
-
- // Comma and first run
- if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
- if ( match ) {
-
- // Don't consume trailing commas as valid
- soFar = soFar.slice( match[ 0 ].length ) || soFar;
- }
- groups.push( ( tokens = [] ) );
- }
-
- matched = false;
-
- // Combinators
- if ( ( match = rcombinators.exec( soFar ) ) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
-
- // Cast descendant combinators to space
- type: match[ 0 ].replace( rtrim, " " )
- } );
- soFar = soFar.slice( matched.length );
- }
-
- // Filters
- for ( type in Expr.filter ) {
- if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
- ( match = preFilters[ type ]( match ) ) ) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
- type: type,
- matches: match
- } );
- soFar = soFar.slice( matched.length );
- }
- }
-
- if ( !matched ) {
- break;
- }
- }
-
- // Return the length of the invalid excess
- // if we're just parsing
- // Otherwise, throw an error or return tokens
- if ( parseOnly ) {
- return soFar.length;
- }
-
- return soFar ?
- selectorError( selector ) :
-
- // Cache the tokens
- tokenCache( selector, groups ).slice( 0 );
-}
-
-function toSelector( tokens ) {
- var i = 0,
- len = tokens.length,
- selector = "";
- for ( ; i < len; i++ ) {
- selector += tokens[ i ].value;
- }
- return selector;
-}
+setFilters.prototype = jQuery.expr.filters = jQuery.expr.pseudos;
+jQuery.expr.setFilters = new setFilters();
function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
} else {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
- outerCache = elem[ expando ] || ( elem[ expando ] = {} );
+ outerCache = elem[ jQuery.expando ] || ( elem[ jQuery.expando ] = {} );
if ( skip && nodeName( elem, skip ) ) {
elem = elem[ dir ] || elem;
}
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
+ if ( postFilter && !postFilter[ jQuery.expando ] ) {
postFilter = setMatcher( postFilter );
}
- if ( postFinder && !postFinder[ expando ] ) {
+ if ( postFinder && !postFinder[ jQuery.expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction( function( seed, results, context, xml ) {
function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
- leadingRelative = Expr.relative[ tokens[ 0 ].type ],
- implicitRelative = leadingRelative || Expr.relative[ " " ],
+ leadingRelative = jQuery.expr.relative[ tokens[ 0 ].type ],
+ implicitRelative = leadingRelative || jQuery.expr.relative[ " " ],
i = leadingRelative ? 1 : 0,
// The foundational matcher ensures that elements are reachable from top-level context(s)
} ];
for ( ; i < len; i++ ) {
- if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
+ if ( ( matcher = jQuery.expr.relative[ tokens[ i ].type ] ) ) {
matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
} else {
- matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
+ matcher = jQuery.expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
// Return special upon seeing a positional matcher
- if ( matcher[ expando ] ) {
+ if ( matcher[ jQuery.expando ] ) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[ j ].type ] ) {
+ if ( jQuery.expr.relative[ tokens[ j ].type ] ) {
break;
}
}
contextBackup = outermostContext,
// We must always have either seed elements or outermost context
- elems = seed || byElement && Expr.find.TAG( "*", outermost ),
+ elems = seed || byElement && jQuery.expr.find.TAG( "*", outermost ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 );
i = match.length;
while ( i-- ) {
cached = matcherFromTokens( match[ i ] );
- if ( cached[ expando ] ) {
+ if ( cached[ jQuery.expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
// Reduce context if the leading compound selector is an ID
tokens = match[ 0 ] = match[ 0 ].slice( 0 );
if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
- context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
+ context.nodeType === 9 && documentIsHTML &&
+ jQuery.expr.relative[ tokens[ 1 ].type ] ) {
- context = ( Expr.find.ID(
- token.matches[ 0 ].replace( runescape, funescape ),
+ context = ( jQuery.expr.find.ID(
+ unescapeSelector( token.matches[ 0 ] ),
context
) || [] )[ 0 ];
if ( !context ) {
token = tokens[ i ];
// Abort if we hit a combinator
- if ( Expr.relative[ ( type = token.type ) ] ) {
+ if ( jQuery.expr.relative[ ( type = token.type ) ] ) {
break;
}
- if ( ( find = Expr.find[ type ] ) ) {
+ if ( ( find = jQuery.expr.find[ type ] ) ) {
// Search, expanding context for leading sibling combinators
if ( ( seed = find(
- token.matches[ 0 ].replace( runescape, funescape ),
+ unescapeSelector( token.matches[ 0 ] ),
rsibling.test( tokens[ 0 ].type ) &&
testContext( context.parentNode ) || context
) ) ) {
--- /dev/null
+import jQuery from "../core.js";
+
+/**
+ * Create key-value caches of limited size
+ * @returns {function(string, object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+function createCache() {
+ var keys = [];
+
+ function cache( key, value ) {
+
+ // Use (key + " ") to avoid collision with native prototype properties
+ // (see https://github.com/jquery/sizzle/issues/157)
+ if ( keys.push( key + " " ) > jQuery.expr.cacheLength ) {
+
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return ( cache[ key + " " ] = value );
+ }
+ return cache;
+}
+
+export default createCache;
--- /dev/null
+import whitespace from "../var/whitespace.js";
+import identifier from "./var/identifier.js";
+import attributes from "./var/attributes.js";
+import pseudos from "./var/pseudos.js";
+
+var filterMatchExpr = {
+ ID: new RegExp( "^#(" + identifier + ")" ),
+ CLASS: new RegExp( "^\\.(" + identifier + ")" ),
+ TAG: new RegExp( "^(" + identifier + "|[*])" ),
+ ATTR: new RegExp( "^" + attributes ),
+ PSEUDO: new RegExp( "^" + pseudos ),
+ CHILD: new RegExp(
+ "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
+ whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
+ whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" )
+};
+
+export default filterMatchExpr;
--- /dev/null
+import rpseudo from "./var/rpseudo.js";
+import filterMatchExpr from "./filterMatchExpr.js";
+import unescapeSelector from "./unescapeSelector.js";
+import selectorError from "./selectorError.js";
+import tokenize from "./tokenize.js";
+
+var preFilter = {
+ ATTR: function( match ) {
+ match[ 1 ] = unescapeSelector( match[ 1 ] );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[ 3 ] = unescapeSelector( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" );
+
+ if ( match[ 2 ] === "~=" ) {
+ match[ 3 ] = " " + match[ 3 ] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ CHILD: function( match ) {
+
+ /* matches from filterMatchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[ 1 ] = match[ 1 ].toLowerCase();
+
+ if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
+
+ // nth-* requires argument
+ if ( !match[ 3 ] ) {
+ selectorError( match[ 0 ] );
+ }
+
+ // numeric x and y parameters for jQuery.expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[ 4 ] = +( match[ 4 ] ?
+ match[ 5 ] + ( match[ 6 ] || 1 ) :
+ 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
+ );
+ match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[ 3 ] ) {
+ selectorError( match[ 0 ] );
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match ) {
+ var excess,
+ unquoted = !match[ 6 ] && match[ 2 ];
+
+ if ( filterMatchExpr.CHILD.test( match[ 0 ] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[ 3 ] ) {
+ match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+
+ // Get excess from tokenize (recursively)
+ ( excess = tokenize( unquoted, true ) ) &&
+
+ // advance to the next closing parenthesis
+ ( excess = unquoted.indexOf( ")", unquoted.length - excess ) -
+ unquoted.length ) ) {
+
+ // excess is a negative index
+ match[ 0 ] = match[ 0 ].slice( 0, excess );
+ match[ 2 ] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+};
+
+export default preFilter;
--- /dev/null
+function selectorError( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+}
+
+export default selectorError;
--- /dev/null
+/**
+ * Checks a node for validity as a jQuery selector context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+ return context && typeof context.getElementsByTagName !== "undefined" && context;
+}
+
+export default testContext;
--- /dev/null
+function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[ i ].value;
+ }
+ return selector;
+}
+
+export default toSelector;
--- /dev/null
+import jQuery from "../core.js";
+import rcomma from "./var/rcomma.js";
+import rleadingCombinator from "./var/rleadingCombinator.js";
+import rtrim from "../var/rtrim.js";
+import createCache from "./createCache.js";
+import selectorError from "./selectorError.js";
+import filterMatchExpr from "./filterMatchExpr.js";
+
+var tokenCache = createCache();
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = jQuery.expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
+ if ( match ) {
+
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[ 0 ].length ) || soFar;
+ }
+ groups.push( ( tokens = [] ) );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
+ matched = match.shift();
+ tokens.push( {
+ value: matched,
+
+ // Cast descendant combinators to space
+ type: match[ 0 ].replace( rtrim, " " )
+ } );
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in filterMatchExpr ) {
+ if ( ( match = jQuery.expr.match[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
+ ( match = preFilters[ type ]( match ) ) ) ) {
+ matched = match.shift();
+ tokens.push( {
+ value: matched,
+ type: type,
+ matches: match
+ } );
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ if ( parseOnly ) {
+ return soFar.length;
+ }
+
+ return soFar ?
+ selectorError( selector ) :
+
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+export default tokenize;
--- /dev/null
+// CSS escapes
+// https://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+import whitespace from "../var/whitespace.js";
+
+var runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
+ "?|\\\\([^\\r\\n\\f])", "g" ),
+ funescape = function( escape, nonHex ) {
+ var high = "0x" + escape.slice( 1 ) - 0x10000;
+
+ if ( nonHex ) {
+
+ // Strip the backslash prefix from a non-hex escape sequence
+ return nonHex;
+ }
+
+ // Replace a hexadecimal escape sequence with the encoded Unicode code point
+ // Support: IE <=11+
+ // For values outside the Basic Multilingual Plane (BMP), manually construct a
+ // surrogate pair
+ return high < 0 ?
+ String.fromCharCode( high + 0x10000 ) :
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ };
+
+function unescapeSelector( sel ) {
+ return sel.replace( runescape, funescape );
+}
+
+export default unescapeSelector;
--- /dev/null
+import whitespace from "../../var/whitespace.js";
+import identifier from "./identifier.js";
+
+// Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors
+export default "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
+
+ // Operator (capture 2)
+ "*([*^$|!~]?=)" + whitespace +
+
+ // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
+ whitespace + "*\\]";
--- /dev/null
+export default "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
+ "loop|multiple|open|readonly|required|scoped";
--- /dev/null
+import whitespace from "../../var/whitespace.js";
+
+// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
+export default "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
+ "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+";
--- /dev/null
+import documentElement from "../../var/documentElement.js";
+
+// Support: IE 9 - 11+
+// IE requires a prefix.
+export default documentElement.matches || documentElement.msMatchesSelector;
--- /dev/null
+import identifier from "./identifier.js";
+import attributes from "./attributes.js";
+
+export default ":(" + identifier + ")(?:\\((" +
+
+ // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+ // 1. quoted (capture 3; capture 4 or capture 5)
+ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+
+ // 2. simple (capture 6)
+ "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+
+ // 3. anything else (capture 2)
+ ".*" +
+ ")\\)|)";
--- /dev/null
+import whitespace from "../../var/whitespace.js";
+
+export default new RegExp( "^" + whitespace + "*," + whitespace + "*" );
--- /dev/null
+import whitespace from "../../var/whitespace.js";
+
+export default new RegExp( whitespace + "|>" );
--- /dev/null
+import whitespace from "../../var/whitespace.js";
+
+export default new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
+ whitespace + "*" );
--- /dev/null
+import pseudos from "./pseudos.js";
+
+export default new RegExp( pseudos );
--- /dev/null
+export default /[+~]/;
"Empty attributes object is not interpreted as a document (trac-8950)" );
} );
-QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "jQuery(selector, context)", function( assert ) {
+QUnit.test( "jQuery(selector, context)", function( assert ) {
assert.expect( 3 );
assert.deepEqual( jQuery( "div p", "#qunit-fixture" ).get(), q( "sndp", "en", "sap" ), "Basic selector with string as context" );
assert.deepEqual( jQuery( "div p", q( "qunit-fixture" )[ 0 ] ).get(), q( "sndp", "en", "sap" ), "Basic selector with element as context" );
assert.deepEqual( jQuery( "input[id='idTest']", lengthtest ).get(), q( "idTest" ),
"Finding elements with id of ID." );
- if ( QUnit.jQuerySelectors ) {
- siblingTest = document.getElementById( "siblingTest" );
- assert.deepEqual( jQuery( "div em", siblingTest ).get(), [],
- "Element-rooted QSA does not select based on document context" );
- assert.deepEqual( jQuery( "div em, div em, div em:not(div em)", siblingTest ).get(), [],
- "Element-rooted QSA does not select based on document context" );
- assert.deepEqual( jQuery( "div em, em\\,", siblingTest ).get(), [],
- "Escaped commas do not get treated with an id in element-rooted QSA" );
- } else {
- assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" );
- assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" );
- assert.ok( "skip", "Element-rooted QSA behavior different in selector-native" );
- }
+ siblingTest = document.getElementById( "siblingTest" );
+ assert.deepEqual( jQuery( "div em", siblingTest ).get(), [],
+ "Element-rooted QSA does not select based on document context" );
+ assert.deepEqual( jQuery( "div em, div em, div em:not(div em)", siblingTest ).get(), [],
+ "Element-rooted QSA does not select based on document context" );
+ assert.deepEqual( jQuery( "div em, em\\,", siblingTest ).get(), [],
+ "Escaped commas do not get treated with an id in element-rooted QSA" );
html = "";
for ( i = 0; i < 100; i++ ) {
fiddle = jQuery( "<div id='fiddle\\Foo'><span id='fiddleSpan'></span></div>" )
.appendTo( "#qunit-fixture" );
- if ( QUnit.jQuerySelectors ) {
- assert.deepEqual( jQuery( "> span", jQuery( "#fiddle\\\\Foo" )[ 0 ] ).get(),
- q( [ "fiddleSpan" ] ), "Escaped ID as context" );
- } else {
- assert.ok( "skip", "leading > not supported in selector-native" );
- }
+ assert.deepEqual( jQuery( "> span", jQuery( "#fiddle\\\\Foo" )[ 0 ] ).get(),
+ q( [ "fiddleSpan" ] ), "Escaped ID as context" );
fiddle.remove();
siblingFirst = document.getElementById( "siblingfirst" );
- if ( QUnit.jQuerySelectors ) {
- assert.deepEqual( jQuery( "+ em", siblingFirst ).get(), q( "siblingnext" ),
- "Element Directly Preceded By with a context." );
- assert.deepEqual( jQuery( "~ em", siblingFirst ).get(), q( "siblingnext", "siblingthird" ),
- "Element Preceded By with a context." );
- } else {
- assert.ok( "skip", "leading + not supported in selector-native" );
- assert.ok( "skip", "leading ~ not supported in selector-native" );
- }
+ assert.deepEqual( jQuery( "+ em", siblingFirst ).get(), q( "siblingnext" ),
+ "Element Directly Preceded By with a context." );
+ assert.deepEqual( jQuery( "~ em", siblingFirst ).get(), q( "siblingnext", "siblingthird" ),
+ "Element Preceded By with a context." );
if ( QUnit.jQuerySelectorsPos ) {
assert.deepEqual( jQuery( "~ em:first", siblingFirst ).get(), q( "siblingnext" ),
assert.ok( "skip", "Positional selectors are not supported" );
}
- if ( QUnit.jQuerySelectors ) {
- en = document.getElementById( "en" );
- assert.deepEqual( jQuery( "+ p, a", en ).get(), q( "yahoo", "sap" ),
- "Compound selector with context, beginning with sibling test." );
- assert.deepEqual( jQuery( "a, + p", en ).get(), q( "yahoo", "sap" ),
- "Compound selector with context, containing sibling test." );
- } else {
- assert.ok( "skip", "leading + not supported in selector-native" );
- assert.ok( "skip", "leading + not supported in selector-native" );
- }
+ en = document.getElementById( "en" );
+ assert.deepEqual( jQuery( "+ p, a", en ).get(), q( "yahoo", "sap" ),
+ "Compound selector with context, beginning with sibling test." );
+ assert.deepEqual( jQuery( "a, + p", en ).get(), q( "yahoo", "sap" ),
+ "Compound selector with context, containing sibling test." );
if ( QUnit.jQuerySelectors ) {
assert.t( "Element Preceded By, Containing", "#liveHandlerOrder ~ div em:contains('1')", [ "siblingfirst" ] );
"disabled-select", "disabled-optgroup", "disabled-option" ]
);
- if ( QUnit.jQuerySelectors ) {
- assert.t(
- "Enabled elements",
- "#enabled-fieldset :enabled",
- [ "enabled-input", "enabled-textarea", "enabled-button",
- "enabled-select", "enabled-optgroup", "enabled-option" ]
- );
- } else {
- assert.ok( "skip", ":enabled broken in Chrome in selector-native" );
- }
+ assert.t(
+ "Enabled elements",
+ "#enabled-fieldset :enabled",
+ [ "enabled-input", "enabled-textarea", "enabled-button",
+ "enabled-select", "enabled-optgroup", "enabled-option" ]
+ );
} );
QUnit.test( "pseudo - :(dis|en)abled, optgroup and option", function( assert ) {
context = document.getElementById( "nothiddendiv" );
assert.deepEqual( jQuery( "*", context ).get(), q( "nothiddendivchild" ), "<div> context" );
- if ( QUnit.jQuerySelectors ) {
- assert.deepEqual( jQuery( "* > *", context ).get(), [], "<div> context (no results)" );
- } else {
- assert.ok( "skip", "The whole selector not required to be under context in selector-native" );
- }
+ assert.deepEqual( jQuery( "* > *", context ).get(), [], "<div> context (no results)" );
context.removeAttribute( "id" );
assert.deepEqual( jQuery( "*", context ).get(), q( "nothiddendivchild" ), "no-id element context" );
expected = q( "siblingnext", "siblingspan" );
context.appendChild( document.getElementById( "siblingTest" ) );
- if ( QUnit.jQuerySelectors ) {
- assert.deepEqual(
- jQuery( "em:nth-child(2)", context ).get(),
- expected.slice( 0, 1 ),
- "DocumentFragment context"
- );
- assert.deepEqual( jQuery( "span", context ).get(), expected.slice( 1 ),
- "DocumentFragment context by tag name" );
- assert.deepEqual( jQuery( "p", context ).get(), [], "DocumentFragment context (no results)" );
- } else {
- assert.ok( "skip", "selection on document fragments not supported in selector-native" );
- assert.ok( "skip", "selection on document fragments not supported in selector-native" );
- assert.ok( "skip", "selection on document fragments not supported in selector-native" );
- }
+ assert.deepEqual(
+ jQuery( "em:nth-child(2)", context ).get(),
+ expected.slice( 0, 1 ),
+ "DocumentFragment context"
+ );
+ assert.deepEqual( jQuery( "span", context ).get(), expected.slice( 1 ),
+ "DocumentFragment context by tag name" );
+ assert.deepEqual( jQuery( "p", context ).get(), [], "DocumentFragment context (no results)" );
if ( QUnit.jQuerySelectors ) {
assert.deepEqual(
// Support: IE 11+
// IE doesn't support Shadow DOM.
-// selector-native doesn't support querying inside of Shadow DOM.
-QUnit[ QUnit.jQuerySelectors && !QUnit.isIE ? "test" : "skip" ](
- "Shadow DOM nodes supported as root", function( assert ) {
+QUnit.testUnlessIE( "Shadow DOM nodes supported as root", function( assert ) {
assert.expect( 2 );
var shadowHost = jQuery( "<div></div>" ).appendTo( "#qunit-fixture" )[ 0 ];
assert.equal( j.find( "div" ).addBack().length, 3, "Check node,textnode,comment to find zero divs, but preserves pushStack" );
} );
-QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "find(leading combinator)", function( assert ) {
+QUnit.test( "find(leading combinator)", function( assert ) {
assert.expect( 4 );
assert.deepEqual( jQuery( "#qunit-fixture" ).find( "> div" ).get(), q( "foo", "nothiddendiv", "moretests", "tabindex-tests", "liveHandlerOrder", "siblingTest", "fx-test-group" ), "find child elements" );
assert.ok( $child.closest( $body.add( $parent ) ).is( "#nothiddendiv" ), "Closest ancestor retrieved." );
} );
-QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "not(Selector)", function( assert ) {
+// Support: IE 11+
+// IE doesn't support complex selectors inside `:not()`.
+QUnit.testUnlessIE( "not(Selector)", function( assert ) {
assert.expect( 7 );
assert.equal( jQuery( "#qunit-fixture > p#ap > a" ).not( "#google" ).length, 2, "not('selector')" );
assert.deepEqual( jQuery( "#ap *" ).not( "code, #mark" ).get(), q( "google", "groups", "anchor1" ), "not('tag, ID selector')" );
assert.deepEqual( jQuery( "#ap *" ).not( "#mark, code" ).get(), q( "google", "groups", "anchor1" ), "not('ID, tag selector')" );
- assert.deepEqual(
- jQuery( "#form option" ).not( "option.emptyopt:contains('Nothing'),optgroup *,[value='1']" ).get(),
- q( "option1c", "option1d", "option2c", "option2d", "option3c", "option3d", "option3e", "option4d", "option4e", "option5a", "option5b" ),
- "not('complex selector')"
- );
+ if ( QUnit.jQuerySelectors ) {
+ assert.deepEqual(
+ jQuery( "#form option" ).not( "option.emptyopt:contains('Nothing'),optgroup *,[value='1']" ).get(),
+ q( "option1c", "option1d", "option2c", "option2d", "option3c", "option3d", "option3e", "option4d", "option4e", "option5a", "option5b" ),
+ "not('complex selector')"
+ );
+ } else {
+ assert.ok( "skip", ":contains not supported in selector-native" );
+ }
} );
QUnit.test( "not(undefined)", function( assert ) {
);
} );
-QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "not(Selector) excludes non-element nodes (gh-2808)", function( assert ) {
+// Support: IE 11+
+// IE doesn't support complex selectors inside `:not()`.
+QUnit.testUnlessIE( "not(Selector) excludes non-element nodes (gh-2808)", function( assert ) {
assert.expect( 3 );
var mixedContents = jQuery( "#nonnodes" ).contents(),