mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Selector: Backport jQuery selection context logic to selector-native
This makes: ```js $div.find("div > *") ``` no longer matching children of `$div`. Also, leading combinators now work, e.g.: ```js $div.find( "> *" ); ``` returns children of `$div`. As a result of that, a number of tests are no longer skipped in the `selector-native` mode. Also, rename `rcombinators` to `rleadingCombinator`. Fixes gh-5185 Closes gh-5186 Ref gh-5085
This commit is contained in:
parent
7e7bd06207
commit
2e644e8450
@ -8,13 +8,10 @@
|
||||
* * 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)
|
||||
@ -26,20 +23,35 @@
|
||||
|
||||
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 || [];
|
||||
@ -51,7 +63,7 @@ jQuery.extend( {
|
||||
}
|
||||
|
||||
// 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 [];
|
||||
}
|
||||
|
||||
@ -62,18 +74,65 @@ jQuery.extend( {
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
} );
|
||||
|
||||
|
380
src/selector.js
380
src/selector.js
@ -1,7 +1,6 @@
|
||||
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";
|
||||
@ -9,19 +8,31 @@ import whitespace from "./var/whitespace.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
|
||||
@ -30,67 +41,20 @@ var i,
|
||||
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()
|
||||
@ -98,7 +62,7 @@ var i,
|
||||
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,
|
||||
@ -106,30 +70,6 @@ var 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"
|
||||
@ -145,10 +85,6 @@ var i,
|
||||
{ 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,
|
||||
@ -223,10 +159,11 @@ function find( selector, context, results, seed ) {
|
||||
// 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
|
||||
@ -237,7 +174,7 @@ function find( selector, context, results, seed ) {
|
||||
if ( ( nid = context.getAttribute( "id" ) ) ) {
|
||||
nid = jQuery.escapeSelector( nid );
|
||||
} else {
|
||||
context.setAttribute( "id", ( nid = expando ) );
|
||||
context.setAttribute( "id", ( nid = jQuery.expando ) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +222,7 @@ function find( selector, context, results, seed ) {
|
||||
} catch ( qsaError ) {
|
||||
nonnativeSelectorCache( selector, true );
|
||||
} finally {
|
||||
if ( nid === expando ) {
|
||||
if ( nid === jQuery.expando ) {
|
||||
context.removeAttribute( "id" );
|
||||
}
|
||||
}
|
||||
@ -297,35 +234,12 @@ function find( selector, context, results, seed ) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -427,15 +341,6 @@ function createPositionalPseudo( 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
|
||||
@ -491,7 +396,7 @@ find.matchesSelector = function( elem, expr ) {
|
||||
return find( expr, document, null, [ elem ] ).length > 0;
|
||||
};
|
||||
|
||||
Expr = jQuery.expr = {
|
||||
jQuery.expr = {
|
||||
|
||||
// Can be adjusted by the user
|
||||
cacheLength: 50,
|
||||
@ -532,99 +437,18 @@ Expr = jQuery.expr = {
|
||||
"~": { 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() {
|
||||
@ -739,7 +563,8 @@ Expr = jQuery.expr = {
|
||||
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 ];
|
||||
@ -761,7 +586,8 @@ Expr = jQuery.expr = {
|
||||
|
||||
// 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;
|
||||
@ -782,8 +608,8 @@ Expr = jQuery.expr = {
|
||||
|
||||
// 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 ];
|
||||
}
|
||||
|
||||
@ -808,13 +634,14 @@ Expr = jQuery.expr = {
|
||||
// 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 );
|
||||
}
|
||||
|
||||
@ -834,7 +661,7 @@ Expr = jQuery.expr = {
|
||||
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, [] ),
|
||||
@ -865,7 +692,7 @@ Expr = jQuery.expr = {
|
||||
} ),
|
||||
|
||||
contains: markFunction( function( text ) {
|
||||
text = text.replace( runescape, funescape );
|
||||
text = unescapeSelector( text );
|
||||
return function( elem ) {
|
||||
return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
|
||||
};
|
||||
@ -884,7 +711,7 @@ Expr = jQuery.expr = {
|
||||
if ( !ridentifier.test( lang || "" ) ) {
|
||||
selectorError( "unsupported lang: " + lang );
|
||||
}
|
||||
lang = lang.replace( runescape, funescape ).toLowerCase();
|
||||
lang = unescapeSelector( lang ).toLowerCase();
|
||||
return function( elem ) {
|
||||
var elemLang;
|
||||
do {
|
||||
@ -958,7 +785,7 @@ Expr = jQuery.expr = {
|
||||
},
|
||||
|
||||
parent: function( elem ) {
|
||||
return !Expr.pseudos.empty( elem );
|
||||
return !jQuery.expr.pseudos.empty( elem );
|
||||
},
|
||||
|
||||
// Element/input types
|
||||
@ -1035,102 +862,20 @@ Expr = jQuery.expr = {
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
@ -1168,7 +913,7 @@ function addCombinator( matcher, combinator, base ) {
|
||||
} 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;
|
||||
@ -1239,10 +984,10 @@ function condense( unmatched, map, filter, context, xml ) {
|
||||
}
|
||||
|
||||
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 ) {
|
||||
@ -1340,8 +1085,8 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
|
||||
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)
|
||||
@ -1364,18 +1109,18 @@ function matcherFromTokens( tokens ) {
|
||||
} ];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1412,7 +1157,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
|
||||
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 );
|
||||
@ -1537,7 +1282,7 @@ function compile( selector, match /* Internal Use Only */ ) {
|
||||
i = match.length;
|
||||
while ( i-- ) {
|
||||
cached = matcherFromTokens( match[ i ] );
|
||||
if ( cached[ expando ] ) {
|
||||
if ( cached[ jQuery.expando ] ) {
|
||||
setMatchers.push( cached );
|
||||
} else {
|
||||
elementMatchers.push( cached );
|
||||
@ -1577,10 +1322,11 @@ function select( selector, context, results, seed ) {
|
||||
// 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 ) {
|
||||
@ -1600,14 +1346,14 @@ function select( selector, context, results, seed ) {
|
||||
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
|
||||
) ) ) {
|
||||
|
26
src/selector/createCache.js
Normal file
26
src/selector/createCache.js
Normal file
@ -0,0 +1,26 @@
|
||||
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;
|
18
src/selector/filterMatchExpr.js
Normal file
18
src/selector/filterMatchExpr.js
Normal file
@ -0,0 +1,18 @@
|
||||
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;
|
90
src/selector/preFilter.js
Normal file
90
src/selector/preFilter.js
Normal file
@ -0,0 +1,90 @@
|
||||
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;
|
5
src/selector/selectorError.js
Normal file
5
src/selector/selectorError.js
Normal file
@ -0,0 +1,5 @@
|
||||
function selectorError( msg ) {
|
||||
throw new Error( "Syntax error, unrecognized expression: " + msg );
|
||||
}
|
||||
|
||||
export default selectorError;
|
10
src/selector/testContext.js
Normal file
10
src/selector/testContext.js
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* 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;
|
11
src/selector/toSelector.js
Normal file
11
src/selector/toSelector.js
Normal file
@ -0,0 +1,11 @@
|
||||
function toSelector( tokens ) {
|
||||
var i = 0,
|
||||
len = tokens.length,
|
||||
selector = "";
|
||||
for ( ; i < len; i++ ) {
|
||||
selector += tokens[ i ].value;
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
export default toSelector;
|
83
src/selector/tokenize.js
Normal file
83
src/selector/tokenize.js
Normal file
@ -0,0 +1,83 @@
|
||||
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;
|
29
src/selector/unescapeSelector.js
Normal file
29
src/selector/unescapeSelector.js
Normal file
@ -0,0 +1,29 @@
|
||||
// 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;
|
12
src/selector/var/attributes.js
Normal file
12
src/selector/var/attributes.js
Normal file
@ -0,0 +1,12 @@
|
||||
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 + "*\\]";
|
2
src/selector/var/booleans.js
Normal file
2
src/selector/var/booleans.js
Normal file
@ -0,0 +1,2 @@
|
||||
export default "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
|
||||
"loop|multiple|open|readonly|required|scoped";
|
5
src/selector/var/identifier.js
Normal file
5
src/selector/var/identifier.js
Normal file
@ -0,0 +1,5 @@
|
||||
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])+";
|
5
src/selector/var/matches.js
Normal file
5
src/selector/var/matches.js
Normal file
@ -0,0 +1,5 @@
|
||||
import documentElement from "../../var/documentElement.js";
|
||||
|
||||
// Support: IE 9 - 11+
|
||||
// IE requires a prefix.
|
||||
export default documentElement.matches || documentElement.msMatchesSelector;
|
15
src/selector/var/pseudos.js
Normal file
15
src/selector/var/pseudos.js
Normal file
@ -0,0 +1,15 @@
|
||||
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)
|
||||
".*" +
|
||||
")\\)|)";
|
3
src/selector/var/rcomma.js
Normal file
3
src/selector/var/rcomma.js
Normal file
@ -0,0 +1,3 @@
|
||||
import whitespace from "../../var/whitespace.js";
|
||||
|
||||
export default new RegExp( "^" + whitespace + "*," + whitespace + "*" );
|
3
src/selector/var/rdescend.js
Normal file
3
src/selector/var/rdescend.js
Normal file
@ -0,0 +1,3 @@
|
||||
import whitespace from "../../var/whitespace.js";
|
||||
|
||||
export default new RegExp( whitespace + "|>" );
|
4
src/selector/var/rleadingCombinator.js
Normal file
4
src/selector/var/rleadingCombinator.js
Normal file
@ -0,0 +1,4 @@
|
||||
import whitespace from "../../var/whitespace.js";
|
||||
|
||||
export default new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
|
||||
whitespace + "*" );
|
3
src/selector/var/rpseudo.js
Normal file
3
src/selector/var/rpseudo.js
Normal file
@ -0,0 +1,3 @@
|
||||
import pseudos from "./pseudos.js";
|
||||
|
||||
export default new RegExp( pseudos );
|
1
src/selector/var/rsibling.js
Normal file
1
src/selector/var/rsibling.js
Normal file
@ -0,0 +1 @@
|
||||
export default /[+~]/;
|
@ -153,7 +153,7 @@ QUnit.test( "jQuery()", function( assert ) {
|
||||
"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" );
|
||||
|
@ -121,19 +121,13 @@ QUnit.test( "element", function( assert ) {
|
||||
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++ ) {
|
||||
@ -263,12 +257,8 @@ QUnit.test( "id", function( assert ) {
|
||||
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();
|
||||
|
||||
@ -473,15 +463,10 @@ QUnit.test( "child and adjacent", function( assert ) {
|
||||
|
||||
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" ),
|
||||
@ -490,16 +475,11 @@ QUnit.test( "child and adjacent", function( assert ) {
|
||||
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" ] );
|
||||
@ -1412,16 +1392,12 @@ QUnit.test( "pseudo - :(dis|en)abled, explicitly disabled", function( assert ) {
|
||||
"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 ) {
|
||||
@ -1637,11 +1613,7 @@ QUnit.test( "context", 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" );
|
||||
@ -1670,20 +1642,14 @@ QUnit.test( "context", function( assert ) {
|
||||
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(
|
||||
@ -1775,9 +1741,7 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "disconnected nodes", function
|
||||
|
||||
// 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 ];
|
||||
|
@ -13,7 +13,7 @@ QUnit.test( "find(String) under non-elements", function( assert ) {
|
||||
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" );
|
||||
@ -445,7 +445,9 @@ QUnit.test( "closest(jQuery)", function( assert ) {
|
||||
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')" );
|
||||
|
||||
@ -478,11 +480,15 @@ QUnit[ QUnit.jQuerySelectors ? "test" : "skip" ]( "not(Selector)", function( ass
|
||||
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 ) {
|
||||
@ -525,7 +531,9 @@ QUnit.test( "not(jQuery)", 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(),
|
||||
|
Loading…
Reference in New Issue
Block a user