diff options
Diffstat (limited to 'src/selector.js')
-rw-r--r-- | src/selector.js | 380 |
1 files changed, 63 insertions, 317 deletions
diff --git a/src/selector.js b/src/selector.js index 117ee3051..328eca45f 100644 --- a/src/selector.js +++ b/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" ); } } @@ -298,34 +235,11 @@ function find( selector, 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; } @@ -428,15 +342,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 ) ) ) { |